@getmcpads/meta-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.
- package/LICENSE +201 -0
- package/NOTICE +17 -0
- package/README.md +358 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +9497 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +9487 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/schema.ts","../src/core/logger.ts","../src/config/index.ts","../src/server.ts","../src/platforms/meta/tools.ts","../src/core/errors.ts","../src/core/rate-limiter.ts","../src/platforms/meta/client.ts","../src/platforms/meta/metric-catalog.ts","../src/platforms/meta/breakdown-catalog.ts","../src/platforms/meta/compatibility-rules.ts","../src/platforms/meta/dimension-catalog.ts","../src/platforms/meta/query-planner.ts","../src/platforms/meta/types.ts","../src/platforms/meta/calculated-metrics.ts","../src/platforms/meta/broad-read.ts","../src/platforms/meta/resources.ts","../src/platforms/meta/writes.ts","../src/platforms/meta/index.ts","../src/transports/stdio.ts","../src/cli.ts"],"sourcesContent":["/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { z } from \"zod\";\n\nconst metaSchema = z.object({\n accessToken: z.string().min(1),\n apiVersion: z.string().optional(),\n enableWrites: z.boolean().optional(),\n});\n\nexport const serverConfigSchema = z.object({\n meta: metaSchema.optional(),\n logLevel: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).default(\"info\"),\n});\n\nexport type ValidatedConfig = z.infer<typeof serverConfigSchema>;\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nlet currentLevel: LogLevel = (process.env[\"LOG_LEVEL\"] as LogLevel) ?? \"info\";\n\nfunction shouldLog(level: LogLevel): boolean {\n return LEVEL_ORDER[level] >= LEVEL_ORDER[currentLevel];\n}\n\nfunction log(level: LogLevel, platform: string | null, message: string, data?: unknown): void {\n if (!shouldLog(level)) return;\n\n const entry = {\n ts: new Date().toISOString(),\n level,\n ...(platform && { platform }),\n msg: message,\n ...(data !== undefined && { data }),\n };\n\n // MCP servers must only write protocol messages to stdout.\n // All logs go to stderr.\n console.error(JSON.stringify(entry));\n}\n\nexport const logger = {\n debug: (platform: string, msg: string, data?: unknown) => log(\"debug\", platform, msg, data),\n info: (platform: string, msg: string, data?: unknown) => log(\"info\", platform, msg, data),\n warn: (platform: string, msg: string, data?: unknown) => log(\"warn\", platform, msg, data),\n error: (platform: string, msg: string, data?: unknown) => log(\"error\", platform, msg, data),\n system: (msg: string, data?: unknown) => log(\"info\", null, msg, data),\n setLevel: (level: LogLevel) => {\n currentLevel = level;\n },\n};\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { serverConfigSchema } from \"./schema.js\";\nimport type { ServerConfig } from \"./types.js\";\nimport { logger } from \"../core/logger.js\";\n\n/**\n * Load configuration from environment variables.\n *\n * `META_ACCESS_TOKEN` is the only required variable. Everything else is\n * optional and has a sensible default.\n */\nexport function loadConfig(): ServerConfig {\n const env = process.env;\n\n const raw: Record<string, unknown> = {\n logLevel: env[\"LOG_LEVEL\"] ?? \"info\",\n };\n\n if (env[\"META_ACCESS_TOKEN\"]) {\n raw.meta = {\n accessToken: env[\"META_ACCESS_TOKEN\"],\n ...(env[\"META_API_VERSION\"] && { apiVersion: env[\"META_API_VERSION\"] }),\n enableWrites: isTruthy(env[\"META_ENABLE_WRITES\"]),\n };\n }\n\n const result = serverConfigSchema.safeParse(raw);\n if (!result.success) {\n logger.error(\"config\", \"Invalid configuration\", result.error.flatten());\n throw new Error(`Invalid configuration: ${result.error.message}`);\n }\n\n const config = result.data as ServerConfig;\n\n if (!config.meta) {\n logger.warn(\n \"config\",\n \"META_ACCESS_TOKEN is not set. The server will start but expose no tools.\",\n );\n }\n\n return config;\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\nexport type { ServerConfig } from \"./types.js\";\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) 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 { ServerConfig } from \"./config/types.js\";\nimport { registerMeta } from \"./platforms/meta/index.js\";\nimport { logger } from \"./core/logger.js\";\n\nexport const PACKAGE_VERSION = \"1.0.0\";\n\n/**\n * Create and configure the MCP server.\n */\nexport function createServer(config: ServerConfig): McpServer {\n const server = new McpServer(\n {\n name: \"meta-ads-mcp\",\n version: PACKAGE_VERSION,\n },\n {\n capabilities: {\n tools: { listChanged: true },\n resources: { subscribe: false, listChanged: true },\n },\n },\n );\n\n if (config.meta) {\n registerMeta(server, config.meta);\n logger.system(\n `meta-ads-mcp v${PACKAGE_VERSION} ready, writes ${\n config.meta.enableWrites ? \"enabled\" : \"disabled\"\n }`,\n );\n } else {\n logger.system(`meta-ads-mcp v${PACKAGE_VERSION} ready, no credentials set, no tools exposed`);\n }\n\n return server;\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) 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 { MetaApiException, MetaClient } from \"./client.js\";\nimport { validateQuerySelection } from \"./compatibility-rules.js\";\nimport { planQueries, mergeResults } from \"./query-planner.js\";\nimport { calculateDerivedMetrics } from \"./calculated-metrics.js\";\nimport { formatMcpToolError } from \"../../core/errors.js\";\nimport type { MetaConfig } from \"../../config/types.js\";\nimport { registerMetaBroadReadTools } from \"./broad-read.js\";\n\n// Shared schemas\nconst adAccountIdSchema = z.string().describe(\"Ad account ID (e.g., act_123456789)\");\nconst levelSchema = z.enum([\"account\", \"campaign\", \"adset\", \"ad\"]);\nconst statusFilterSchema = z\n .enum([\"ACTIVE\", \"PAUSED\", \"DELETED\", \"ARCHIVED\"])\n .optional()\n .describe(\"Filter by entity status\");\nconst limitSchema = z.number().int().min(1).max(500).optional().default(100);\nconst cursorSchema = z.string().optional().describe(\"Pagination cursor from previous response\");\nconst productBreakdownSchema = z.enum([\n \"product_id\",\n \"product_brand_breakdown\",\n \"product_category_breakdown\",\n \"product_type_breakdown\",\n \"product_custom_label_0_breakdown\",\n \"product_custom_label_1_breakdown\",\n \"product_custom_label_2_breakdown\",\n \"product_custom_label_3_breakdown\",\n \"product_custom_label_4_breakdown\",\n \"product_group_content_id_breakdown\",\n]);\n\nconst datePresetSchema = z\n .enum([\n \"today\", \"yesterday\", \"this_month\", \"last_month\", \"this_quarter\",\n \"maximum\", \"data_maximum\", \"last_3d\", \"last_7d\", \"last_14d\",\n \"last_28d\", \"last_30d\", \"last_90d\", \"last_week_mon_sun\",\n \"last_week_sun_sat\", \"last_quarter\", \"last_year\",\n \"this_week_mon_today\", \"this_week_sun_today\", \"this_year\",\n ])\n .optional();\n\nconst timeRangeSchema = z\n .object({\n since: z.string().describe(\"Start date YYYY-MM-DD\"),\n until: z.string().describe(\"End date YYYY-MM-DD\"),\n })\n .optional();\n\nconst META_GRAPH_BASE = \"https://graph.facebook.com\";\nconst REQUIRED_READ_SCOPES = [\"ads_read\"] as const;\nconst RECOMMENDED_READ_SCOPES = [\n \"business_management\",\n \"pages_show_list\",\n \"pages_read_engagement\",\n \"instagram_basic\",\n] as const;\n\ntype MetaWarning = {\n area: string;\n message: string;\n suggestion: string;\n code?: number;\n type?: string;\n subcode?: number;\n fbtraceId?: string;\n};\n\nlet responseApiVersion: string | null = null;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction hasOwnField(value: Record<string, unknown>, field: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, field);\n}\n\nfunction getDebugRecord(payload: Record<string, unknown>): Record<string, unknown> {\n return isRecord(payload[\"debug\"]) ? payload[\"debug\"] : {};\n}\n\nfunction getRequestCount(payload: Record<string, unknown>): number {\n const debug = getDebugRecord(payload);\n const requestCount = debug[\"requestCount\"];\n return typeof requestCount === \"number\" ? requestCount : 1;\n}\n\nfunction getWarnings(payload: Record<string, unknown>): 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 (!isRecord(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: \"meta_ads\",\n apiVersion: responseApiVersion,\n requestCount: getRequestCount(data),\n },\n };\n}\n\nfunction redactSensitive(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => redactSensitive(item));\n }\n\n if (isRecord(value)) {\n const redacted: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n const lowerKey = key.toLowerCase();\n if (\n lowerKey === \"access_token\" ||\n lowerKey === \"input_token\" ||\n lowerKey === \"appsecret_proof\" ||\n lowerKey === \"client_secret\" ||\n lowerKey.endsWith(\"_access_token\")\n ) {\n redacted[key] = \"[REDACTED]\";\n } else {\n redacted[key] = redactSensitive(item);\n }\n }\n return redacted;\n }\n\n if (typeof value === \"string\" && value.includes(\"access_token=\")) {\n try {\n const url = new URL(value);\n if (url.searchParams.has(\"access_token\")) {\n url.searchParams.set(\"access_token\", \"[REDACTED]\");\n }\n return url.toString();\n } catch {\n return value.replace(/access_token=([^&\\s]+)/g, \"access_token=[REDACTED]\");\n }\n }\n\n return value;\n}\n\nfunction ok(data: unknown) {\n return { content: [{ type: \"text\" as const, text: JSON.stringify(redactSensitive(withAgentResponseContract(data)), null, 2) }] };\n}\n\nfunction formatAdAccountId(adAccountId: string): string {\n return adAccountId.startsWith(\"act_\") ? adAccountId : `act_${adAccountId}`;\n}\n\nfunction graphUrl(\n client: MetaClient,\n path: string,\n params: Record<string, string | number | boolean | undefined> = {},\n): string {\n const normalizedPath = path.replace(/^\\/+/, \"\");\n const base = normalizedPath\n ? `${META_GRAPH_BASE}/${client.apiVersion}/${normalizedPath}`\n : `${META_GRAPH_BASE}/${client.apiVersion}/`;\n const url = new URL(base);\n\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== \"\") {\n url.searchParams.set(key, String(value));\n }\n }\n\n return url.toString();\n}\n\nfunction warningFromError(area: string, error: unknown, suggestion: string): MetaWarning {\n if (error instanceof MetaApiException) {\n return {\n area,\n message: error.message,\n suggestion,\n code: error.code,\n type: error.type,\n subcode: error.subcode,\n fbtraceId: error.fbtraceId,\n };\n }\n\n return {\n area,\n message: error instanceof Error ? error.message : String(error),\n suggestion,\n };\n}\n\nasync function fetchGraph(\n client: MetaClient,\n area: string,\n url: string,\n warnings: MetaWarning[],\n suggestion: string,\n): Promise<Record<string, unknown> | null> {\n try {\n return await client.fetchUrl(url);\n } catch (error) {\n warnings.push(warningFromError(area, error, suggestion));\n return null;\n }\n}\n\nasync function fetchGraphWithFallback(\n client: MetaClient,\n area: string,\n primaryUrl: string,\n fallbackUrl: string,\n warnings: MetaWarning[],\n suggestion: string,\n): Promise<Record<string, unknown> | null> {\n try {\n return await client.fetchUrl(primaryUrl);\n } catch (primaryError) {\n try {\n const fallback = await client.fetchUrl(fallbackUrl);\n warnings.push({\n ...warningFromError(area, primaryError, suggestion),\n suggestion: `${suggestion} Returned a minimal read-only field set instead.`,\n });\n return fallback;\n } catch (fallbackError) {\n warnings.push(warningFromError(area, fallbackError, suggestion));\n return null;\n }\n }\n}\n\nfunction dataArray(result: Record<string, unknown> | null): Record<string, unknown>[] {\n const data = result?.data;\n return Array.isArray(data) ? data.filter(isRecord) : [];\n}\n\nfunction pagingInfo(result: Record<string, unknown> | null): unknown {\n return isRecord(result?.paging) ? result.paging : undefined;\n}\n\nfunction pictureUrl(value: unknown): string | undefined {\n if (typeof value === \"string\") return value;\n if (!isRecord(value)) return undefined;\n\n const data = value.data;\n if (isRecord(data) && typeof data.url === \"string\") {\n return data.url;\n }\n\n return undefined;\n}\n\nfunction addById(\n target: Map<string, Record<string, unknown>>,\n items: Record<string, unknown>[],\n): void {\n for (const item of items) {\n const id = typeof item.id === \"string\" ? item.id : undefined;\n if (!id) continue;\n target.set(id, { ...(target.get(id) ?? {}), ...item });\n }\n}\n\nfunction normalizePage(page: Record<string, unknown>, source: string): Record<string, unknown> {\n return {\n id: page.id,\n name: page.name,\n category: page.category,\n tasks: page.tasks,\n picture: pictureUrl(page.picture),\n instagram_business_account: page.instagram_business_account,\n connected_instagram_account: page.connected_instagram_account,\n source,\n };\n}\n\nfunction normalizeInstagramAccount(\n account: Record<string, unknown>,\n source: string,\n context: Record<string, unknown> = {},\n): Record<string, unknown> {\n return {\n id: account.id,\n username: account.username,\n name: account.name,\n ig_id: account.ig_id,\n profile_picture_url: account.profile_picture_url ?? pictureUrl(account.profile_picture),\n source,\n ...context,\n };\n}\n\nfunction firstString(...values: unknown[]): string | undefined {\n for (const value of values) {\n if (typeof value === \"string\" && value.length > 0) return value;\n }\n return undefined;\n}\n\nfunction extractCreativeCallToAction(creative: Record<string, unknown>): unknown {\n if (creative.call_to_action_type) return creative.call_to_action_type;\n\n const spec = creative.object_story_spec;\n if (!isRecord(spec)) return undefined;\n\n for (const key of [\"link_data\", \"video_data\", \"template_data\"] as const) {\n const value = spec[key];\n if (isRecord(value) && value.call_to_action) {\n return value.call_to_action;\n }\n }\n\n return undefined;\n}\n\nfunction extractCreativeLinkUrl(creative: Record<string, unknown>): string | undefined {\n const spec = creative.object_story_spec;\n const assetFeed = creative.asset_feed_spec;\n const assetFeedRecord = isRecord(assetFeed) ? assetFeed : undefined;\n const linkData = isRecord(spec) && isRecord(spec.link_data) ? spec.link_data : undefined;\n const videoData = isRecord(spec) && isRecord(spec.video_data) ? spec.video_data : undefined;\n const templateData = isRecord(spec) && isRecord(spec.template_data) ? spec.template_data : undefined;\n const videoCta = isRecord(videoData?.call_to_action) ? videoData.call_to_action : undefined;\n const videoCtaValue = isRecord(videoCta?.value) ? videoCta.value : undefined;\n const linkUrls = assetFeedRecord && Array.isArray(assetFeedRecord.link_urls)\n ? assetFeedRecord.link_urls.filter(isRecord)\n : [];\n const firstAssetLink = linkUrls[0];\n\n return firstString(\n creative.link_url,\n linkData?.link,\n linkData?.template_url,\n templateData?.link,\n templateData?.template_url,\n templateData?.website_url,\n videoCtaValue?.link,\n assetFeedRecord?.template_url,\n firstAssetLink?.website_url,\n firstAssetLink?.url,\n );\n}\n\nfunction normalizeCreativeAsset(\n source: Record<string, unknown>,\n sourceType: \"ad\" | \"creative\",\n): Record<string, unknown> {\n const creative = sourceType === \"ad\" && isRecord(source.creative)\n ? source.creative\n : source;\n const spec = isRecord(creative.object_story_spec) ? creative.object_story_spec : {};\n const linkData = isRecord(spec.link_data) ? spec.link_data : undefined;\n const videoData = isRecord(spec.video_data) ? spec.video_data : undefined;\n const photoData = isRecord(spec.photo_data) ? spec.photo_data : undefined;\n const templateData = isRecord(spec.template_data) ? spec.template_data : undefined;\n\n return {\n source_type: sourceType,\n ad_id: sourceType === \"ad\" ? source.id : undefined,\n ad_name: sourceType === \"ad\" ? source.name : undefined,\n ad_status: sourceType === \"ad\" ? source.status : undefined,\n ad_effective_status: sourceType === \"ad\" ? source.effective_status : undefined,\n creative_id: creative.id,\n creative_name: creative.name,\n thumbnail_url: creative.thumbnail_url,\n image_url: firstString(\n creative.image_url,\n linkData?.image_url,\n videoData?.image_url,\n photoData?.url,\n ),\n video_id: firstString(creative.video_id, videoData?.video_id),\n link_url: extractCreativeLinkUrl(creative),\n product_set_id: firstString(\n creative.product_set_id,\n linkData?.product_set_id,\n templateData?.product_set_id,\n isRecord(creative.asset_feed_spec) ? creative.asset_feed_spec.product_set_id : undefined,\n ),\n call_to_action: extractCreativeCallToAction(creative),\n page_id: spec.page_id,\n instagram_actor_id: spec.instagram_actor_id,\n effective_object_story_id: creative.effective_object_story_id,\n object_type: creative.object_type,\n object_story_spec: creative.object_story_spec,\n asset_feed_spec: creative.asset_feed_spec,\n raw_creative: creative,\n };\n}\n\nfunction entityIssueSummary(entity: Record<string, unknown>): Record<string, unknown> {\n const effectiveStatus = entity.effective_status;\n const issues = entity.issues_info;\n const learning = entity.learning_stage_info;\n const hasIssues = Array.isArray(issues) ? issues.length > 0 : Boolean(issues);\n\n return {\n ...entity,\n delivery_summary: {\n configured_status: entity.configured_status ?? entity.status,\n effective_status: effectiveStatus,\n has_issues: hasIssues,\n issues_info: issues,\n learning_stage_info: learning,\n },\n };\n}\n\nfunction summaryCount(value: unknown): number | undefined {\n if (!isRecord(value) || !isRecord(value.summary)) return undefined;\n const total = value.summary.total_count;\n return typeof total === \"number\" ? total : undefined;\n}\n\nfunction normalizeProductItem(\n product: Record<string, unknown>,\n source: string,\n): Record<string, unknown> {\n const catalog = isRecord(product.product_catalog) ? product.product_catalog : undefined;\n const group = isRecord(product.product_group) ? product.product_group : undefined;\n\n return {\n id: product.id,\n retailer_id: product.retailer_id,\n name: product.name,\n description: product.description,\n brand: product.brand,\n category: product.category ?? product.fb_product_category,\n product_type: product.product_type,\n price: product.price,\n sale_price: product.sale_price,\n currency: product.currency,\n availability: product.availability,\n inventory: product.inventory,\n visibility: product.visibility,\n review_status: product.review_status,\n url: product.url,\n image_url: product.image_url,\n additional_image_urls: product.additional_image_urls,\n custom_label_0: product.custom_label_0,\n custom_label_1: product.custom_label_1,\n custom_label_2: product.custom_label_2,\n custom_label_3: product.custom_label_3,\n custom_label_4: product.custom_label_4,\n product_catalog_id: catalog?.id,\n product_catalog_name: catalog?.name,\n product_group_id: group?.id,\n retailer_product_group_id: product.retailer_product_group_id,\n source,\n raw_product: product,\n };\n}\n\nfunction stringValues(...values: unknown[]): string[] {\n return values\n .flatMap((value) => {\n if (Array.isArray(value)) return value;\n return [value];\n })\n .filter((value): value is string => typeof value === \"string\" && value.length > 0);\n}\n\nfunction productKeysForBreakdown(\n product: Record<string, unknown>,\n breakdown: z.infer<typeof productBreakdownSchema>,\n): string[] {\n const raw = isRecord(product.raw_product) ? product.raw_product : product;\n const productGroup = isRecord(raw.product_group) ? raw.product_group : undefined;\n\n switch (breakdown) {\n case \"product_brand_breakdown\":\n return stringValues(product.brand, raw.brand);\n case \"product_category_breakdown\":\n return stringValues(product.category, raw.category, raw.fb_product_category);\n case \"product_type_breakdown\":\n return stringValues(product.product_type, raw.product_type);\n case \"product_custom_label_0_breakdown\":\n return stringValues(product.custom_label_0, raw.custom_label_0);\n case \"product_custom_label_1_breakdown\":\n return stringValues(product.custom_label_1, raw.custom_label_1);\n case \"product_custom_label_2_breakdown\":\n return stringValues(product.custom_label_2, raw.custom_label_2);\n case \"product_custom_label_3_breakdown\":\n return stringValues(product.custom_label_3, raw.custom_label_3);\n case \"product_custom_label_4_breakdown\":\n return stringValues(product.custom_label_4, raw.custom_label_4);\n case \"product_group_content_id_breakdown\":\n return stringValues(\n product.retailer_product_group_id,\n raw.retailer_product_group_id,\n product.product_group_id,\n productGroup?.id,\n );\n case \"product_id\":\n default:\n return stringValues(product.id, product.retailer_id, raw.id, raw.retailer_id);\n }\n}\n\nfunction buildProductLookup(\n products: Record<string, unknown>[],\n breakdown: z.infer<typeof productBreakdownSchema>,\n): Map<string, Record<string, unknown>[]> {\n const lookup = new Map<string, Record<string, unknown>[]>();\n\n for (const product of products) {\n for (const key of productKeysForBreakdown(product, breakdown)) {\n const existing = lookup.get(key) ?? [];\n existing.push(product);\n lookup.set(key, existing);\n }\n }\n\n return lookup;\n}\n\nfunction summarizeProductGroup(\n key: string,\n products: Record<string, unknown>[],\n): Record<string, unknown> {\n return {\n key,\n product_count: products.length,\n sample_products: products.slice(0, 5),\n };\n}\n\nfunction productSearchMatches(product: Record<string, unknown>, search: string | undefined): boolean {\n if (!search) return true;\n const needle = search.toLowerCase();\n return [\n product.id,\n product.retailer_id,\n product.name,\n product.brand,\n product.category,\n product.product_type,\n product.custom_label_0,\n product.custom_label_1,\n product.custom_label_2,\n product.custom_label_3,\n product.custom_label_4,\n ].some((value) => typeof value === \"string\" && value.toLowerCase().includes(needle));\n}\n\nfunction normalizeMetricKey(key: string): string {\n return key.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n}\n\nfunction numberFromUnknown(value: unknown): number | undefined {\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 : undefined;\n }\n return undefined;\n}\n\nfunction findNumberDeep(value: unknown, candidateKeys: string[]): number | undefined {\n const normalizedCandidates = new Set(candidateKeys.map(normalizeMetricKey));\n const seen = new Set<unknown>();\n\n const visit = (current: unknown): number | undefined => {\n if (current === null || current === undefined || seen.has(current)) return undefined;\n if (typeof current !== \"object\") return undefined;\n seen.add(current);\n\n if (Array.isArray(current)) {\n for (const item of current) {\n const found = visit(item);\n if (found !== undefined) return found;\n }\n return undefined;\n }\n\n if (!isRecord(current)) return undefined;\n\n for (const [key, item] of Object.entries(current)) {\n if (normalizedCandidates.has(normalizeMetricKey(key))) {\n const valueNumber = numberFromUnknown(item);\n if (valueNumber !== undefined) return valueNumber;\n }\n }\n\n for (const item of Object.values(current)) {\n const found = visit(item);\n if (found !== undefined) return found;\n }\n\n return undefined;\n };\n\n return visit(value);\n}\n\nfunction interpretConfidence(confidence: number | undefined): Record<string, unknown> {\n if (confidence === undefined) {\n return {\n band: \"unknown\",\n actionability: \"manual_review\",\n message: \"No confidence metric was exposed by the Graph API response.\",\n };\n }\n\n if (confidence >= 95) {\n return {\n band: \"statistically_significant\",\n actionability: \"act_with_confidence\",\n message: \"Confidence is at or above 95%.\",\n };\n }\n\n if (confidence >= 90) {\n return {\n band: \"likely_significant\",\n actionability: \"act_carefully_or_retest\",\n message: \"Confidence is between 90% and 95%.\",\n };\n }\n\n if (confidence >= 80) {\n return {\n band: \"directional\",\n actionability: \"extend_or_increase_power\",\n message: \"Confidence is directional but below a normal decision threshold.\",\n };\n }\n\n return {\n band: \"inconclusive\",\n actionability: \"do_not_declare_winner\",\n message: \"Confidence is below 80%. Treat the result as inconclusive.\",\n };\n}\n\nfunction interpretStudyObjective(objective: Record<string, unknown>): Record<string, unknown> {\n const confidence = findNumberDeep(objective, [\n \"confidence\",\n \"confidence_level\",\n \"confidenceLevel\",\n \"statistical_confidence\",\n ]);\n const lift = findNumberDeep(objective, [\n \"lift\",\n \"lift_percentage\",\n \"incremental_lift\",\n \"conversion_lift\",\n \"incremental_conversion_lift\",\n ]);\n const incrementalConversions = findNumberDeep(objective, [\n \"incremental_conversions\",\n \"incremental_conversion\",\n \"conversions_incremental\",\n ]);\n const costPerIncrementalConversion = findNumberDeep(objective, [\n \"cost_per_incremental_conversion\",\n \"cpi_conversion\",\n \"cost_per_incremental_action\",\n ]);\n const incrementalValue = findNumberDeep(objective, [\n \"incremental_value\",\n \"incremental_revenue\",\n \"incremental_purchase_value\",\n ]);\n const spend = findNumberDeep(objective, [\"spend\", \"amount_spent\"]);\n const incrementalRoas = incrementalValue !== undefined && spend !== undefined && spend > 0\n ? incrementalValue / spend\n : findNumberDeep(objective, [\"incremental_roas\", \"i_roas\", \"iroas\"]);\n\n return {\n ...objective,\n interpretation: {\n confidence,\n lift_percentage: lift,\n incremental_conversions: incrementalConversions,\n cost_per_incremental_conversion: costPerIncrementalConversion,\n incremental_value: incrementalValue,\n incremental_roas: incrementalRoas,\n confidence_read: interpretConfidence(confidence),\n guardrail:\n \"Do not declare a winner below 90% confidence; use inconclusive or directional language and consider extending or redesigning the test.\",\n },\n };\n}\n\nfunction inferStudyStatus(study: Record<string, unknown>): string {\n if (typeof study.canceled_time === \"string\") return \"canceled\";\n const now = Date.now();\n const start = typeof study.start_time === \"string\" ? Date.parse(study.start_time) : undefined;\n const end = typeof study.end_time === \"string\" ? Date.parse(study.end_time) : undefined;\n if (start !== undefined && Number.isFinite(start) && start > now) return \"scheduled\";\n if (end !== undefined && Number.isFinite(end) && end < now) return \"completed\";\n if (start !== undefined && Number.isFinite(start) && start <= now) return \"running\";\n return \"unknown\";\n}\n\nfunction summarizeBrandSafetyTargeting(adset: Record<string, unknown>): Record<string, unknown> {\n const targeting = isRecord(adset.targeting) ? adset.targeting : {};\n return {\n id: adset.id,\n name: adset.name,\n campaign_id: adset.campaign_id,\n status: adset.status,\n effective_status: adset.effective_status,\n controls: {\n publisher_platforms: targeting.publisher_platforms,\n facebook_positions: targeting.facebook_positions,\n instagram_positions: targeting.instagram_positions,\n audience_network_positions: targeting.audience_network_positions,\n messenger_positions: targeting.messenger_positions,\n device_platforms: targeting.device_platforms,\n excluded_publisher_categories: targeting.excluded_publisher_categories,\n brand_safety_content_filter_levels: targeting.brand_safety_content_filter_levels,\n excluded_brand_safety_content_types: targeting.excluded_brand_safety_content_types,\n },\n };\n}\n\nfunction normalizeOrganicPost(post: Record<string, unknown>): Record<string, unknown> {\n return {\n id: post.id,\n message: post.message,\n created_time: post.created_time,\n type: post.type,\n permalink_url: post.permalink_url,\n full_picture: post.full_picture,\n attachments: post.attachments,\n counts: {\n shares: isRecord(post.shares) ? post.shares.count : undefined,\n likes: summaryCount(post.likes),\n comments: summaryCount(post.comments),\n },\n insights: post.insights,\n raw_post: post,\n };\n}\n\nfunction normalizeInstagramMedia(media: Record<string, unknown>): Record<string, unknown> {\n return {\n id: media.id,\n caption: media.caption,\n media_type: media.media_type,\n media_url: media.media_url,\n thumbnail_url: media.thumbnail_url,\n permalink: media.permalink,\n timestamp: media.timestamp,\n username: media.username,\n like_count: media.like_count,\n comments_count: media.comments_count,\n children: media.children,\n insights: media.insights,\n raw_media: media,\n };\n}\n\nexport function registerMetaTools(server: McpServer, config: MetaConfig): void {\n const client = new MetaClient(config.accessToken, config.apiVersion);\n responseApiVersion = client.apiVersion;\n\n // ── P0/P1. meta_health_check ──────────────────────────────────────\n server.tool(\n \"meta_health_check\",\n \"Agent-ready read-only health check: validates token metadata, lists accessible ad accounts, and warns about missing read scopes without exposing the access token.\",\n {},\n async () => {\n const warnings: MetaWarning[] = [];\n let tokenValid = false;\n let missingRequired: string[] = [];\n let missingRecommended: string[] = [];\n let grantedScopes: string[] = [];\n let tokenCheck: Record<string, unknown> = { ok: false };\n let adAccountCheck: Record<string, unknown> = { ok: false };\n\n try {\n const tokenInfo = await client.debugToken();\n tokenValid = tokenInfo.is_valid;\n const scopes = Array.isArray(tokenInfo.scopes) ? tokenInfo.scopes : [];\n grantedScopes = scopes;\n missingRequired = REQUIRED_READ_SCOPES.filter((scope) => !scopes.includes(scope));\n missingRecommended = RECOMMENDED_READ_SCOPES.filter((scope) => !scopes.includes(scope));\n\n if (!tokenInfo.is_valid) {\n warnings.push({\n area: \"token\",\n message: \"Meta reports the access token as invalid.\",\n suggestion: \"Refresh the token and run meta_health_check again.\",\n });\n }\n\n if (missingRequired.length > 0) {\n warnings.push({\n area: \"scopes\",\n message: `Missing required read scope(s): ${missingRequired.join(\", \")}.`,\n suggestion: \"Grant ads_read for read-only ad account access.\",\n });\n }\n\n if (missingRecommended.length > 0) {\n warnings.push({\n area: \"scopes\",\n message: `Missing recommended discovery scope(s): ${missingRecommended.join(\", \")}.`,\n suggestion:\n \"Grant the missing read-only scopes when you need business, page, Instagram, or pixel discovery.\",\n });\n }\n\n if (grantedScopes.includes(\"ads_management\")) {\n warnings.push({\n area: \"scopes\",\n message: \"ads_management is present but this MCP implementation only performs read-only GET calls.\",\n suggestion: \"Prefer a token without write scopes unless another system explicitly needs ads_management.\",\n });\n }\n\n tokenCheck = {\n ok: true,\n is_valid: tokenInfo.is_valid,\n app_id: tokenInfo.app_id,\n user_id: tokenInfo.user_id,\n expires_at: tokenInfo.expires_at,\n expires_at_iso: tokenInfo.expires_at\n ? new Date(tokenInfo.expires_at * 1000).toISOString()\n : null,\n scopes: grantedScopes,\n missing_required_scopes: missingRequired,\n missing_recommended_scopes: missingRecommended,\n };\n } catch (error) {\n warnings.push(warningFromError(\n \"token\",\n error,\n \"Verify META_ACCESS_TOKEN, app mode, and that the token has not expired.\",\n ));\n }\n\n try {\n const accounts = await client.getAdAccounts();\n const inactiveAccounts = accounts.filter((account) => {\n const status = isRecord(account) ? account[\"account_status\"] : undefined;\n return status !== undefined && String(status) !== \"1\";\n });\n adAccountCheck = {\n ok: true,\n count: accounts.length,\n accounts,\n };\n if (inactiveAccounts.length > 0) {\n warnings.push({\n area: \"ad_accounts\",\n message: `${inactiveAccounts.length} accessible ad account(s) are not ACTIVE (account_status != 1).`,\n suggestion: \"Exclude disabled or non-active ad accounts from automatic account mapping unless the user explicitly selects them.\",\n });\n }\n } catch (error) {\n warnings.push(warningFromError(\n \"ad_accounts\",\n error,\n \"Grant ads_read and confirm the user has access to at least one Meta ad account.\",\n ));\n }\n\n const insightsOk = tokenValid && missingRequired.length === 0 && adAccountCheck.ok === true;\n const discoveryOk = tokenValid && missingRecommended.length === 0;\n\n return ok({\n status: !tokenValid ? \"error\" : missingRequired.length > 0 || adAccountCheck.ok !== true ? \"degraded\" : \"ok\",\n apiVersion: client.apiVersion,\n capabilities: {\n insights: {\n status: insightsOk ? \"ok\" : \"degraded\",\n requiredScopes: [...REQUIRED_READ_SCOPES],\n missingRequiredScopes: missingRequired,\n },\n discovery: {\n status: discoveryOk ? \"ok\" : \"degraded\",\n recommendedScopes: [...RECOMMENDED_READ_SCOPES],\n missingRecommendedScopes: missingRecommended,\n },\n writeSurface: {\n status: grantedScopes.includes(\"ads_management\") ? \"warning\" : \"ok\",\n adsManagementGranted: grantedScopes.includes(\"ads_management\"),\n note: grantedScopes.includes(\"ads_management\")\n ? \"ads_management is granted but this MCP only performs read-only GET calls.\"\n : \"No write scope detected for this read-only MCP.\",\n },\n },\n checks: {\n token: tokenCheck,\n ad_accounts: adAccountCheck,\n },\n warnings,\n });\n },\n );\n\n // ── P0/P1. meta_get_business_assets ───────────────────────────────\n server.tool(\n \"meta_get_business_assets\",\n \"Discover accessible Meta Business assets read-only: businesses, pages, Instagram accounts, pixels, and datasets when permissions allow.\",\n {\n businessId: z.string().optional().describe(\"Business Manager ID. If omitted, the tool lists /me/businesses and uses ad account business metadata when available.\"),\n adAccountId: adAccountIdSchema.optional().describe(\"Optional ad account ID to discover account-level pixels/datasets and related business.\"),\n limit: limitSchema,\n cursor: cursorSchema,\n },\n async ({ businessId, adAccountId, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const businessIds = new Set<string>();\n const businessesById = new Map<string, Record<string, unknown>>();\n const pagesById = new Map<string, Record<string, unknown>>();\n const instagramById = new Map<string, Record<string, unknown>>();\n const pixelsById = new Map<string, Record<string, unknown>>();\n const datasetsById = new Map<string, Record<string, unknown>>();\n\n const businessFields = \"id,name,verification_status,created_time,updated_time\";\n const pageFields = \"id,name,category,tasks,picture{url},instagram_business_account{id,username,name,ig_id,profile_picture_url},connected_instagram_account{id,username,name,ig_id,profile_picture_url}\";\n const pageFallbackFields = \"id,name,category,tasks,picture{url}\";\n const instagramFields = \"id,username,name,ig_id,profile_picture_url\";\n const pixelFields = \"id,name,last_fired_time,creation_time,owner_ad_account,business\";\n const pixelFallbackFields = \"id,name,last_fired_time,creation_time\";\n const datasetFields = \"id,name,description,creation_time,updated_time\";\n const datasetFallbackFields = \"id,name\";\n\n if (businessId) {\n businessIds.add(businessId);\n const business = await fetchGraph(\n client,\n \"business\",\n graphUrl(client, `/${businessId}`, { fields: businessFields }),\n warnings,\n \"Grant business_management or provide a Business ID the token can read.\",\n );\n if (business) businessesById.set(String(business.id ?? businessId), business);\n } else {\n const businesses = await fetchGraph(\n client,\n \"businesses\",\n graphUrl(client, \"/me/businesses\", { fields: businessFields, limit, after: cursor }),\n warnings,\n \"Grant business_management to list Business Manager assets. You can still provide businessId or adAccountId directly.\",\n );\n for (const business of dataArray(businesses)) {\n const id = typeof business.id === \"string\" ? business.id : undefined;\n if (!id) continue;\n businessIds.add(id);\n businessesById.set(id, business);\n }\n }\n\n if (adAccountId) {\n const accountBusiness = await fetchGraph(\n client,\n \"ad_account_business\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}`, {\n fields: \"id,name,business{id,name,verification_status}\",\n }),\n warnings,\n \"Grant ads_read and business access for the selected ad account.\",\n );\n const business = isRecord(accountBusiness?.business) ? accountBusiness.business : undefined;\n if (business && typeof business.id === \"string\") {\n businessIds.add(business.id);\n businessesById.set(business.id, business);\n }\n\n const adAccountPixels = await fetchGraphWithFallback(\n client,\n \"ad_account_pixels\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/adspixels`, {\n fields: pixelFields,\n limit,\n after: cursor,\n }),\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/adspixels`, {\n fields: pixelFallbackFields,\n limit,\n after: cursor,\n }),\n warnings,\n \"Grant ads_read and pixel access on the ad account to read pixel metadata.\",\n );\n addById(pixelsById, dataArray(adAccountPixels));\n\n const offlineDatasets = await fetchGraphWithFallback(\n client,\n \"ad_account_offline_datasets\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/offline_conversion_data_sets`, {\n fields: datasetFields,\n limit,\n after: cursor,\n }),\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/offline_conversion_data_sets`, {\n fields: datasetFallbackFields,\n limit,\n after: cursor,\n }),\n warnings,\n \"Offline dataset access may require business-level permissions; provide businessId if available.\",\n );\n addById(datasetsById, dataArray(offlineDatasets));\n }\n\n for (const id of businessIds) {\n const [\n ownedPages,\n clientPages,\n ownedInstagram,\n clientInstagram,\n ownedPixels,\n clientPixels,\n ownedDatasets,\n clientDatasets,\n ] = await Promise.all([\n fetchGraphWithFallback(\n client,\n `business:${id}:owned_pages`,\n graphUrl(client, `/${id}/owned_pages`, { fields: pageFields, limit, after: cursor }),\n graphUrl(client, `/${id}/owned_pages`, { fields: pageFallbackFields, limit, after: cursor }),\n warnings,\n \"Grant business_management plus pages_show_list/pages_read_engagement to read owned Pages.\",\n ),\n fetchGraphWithFallback(\n client,\n `business:${id}:client_pages`,\n graphUrl(client, `/${id}/client_pages`, { fields: pageFields, limit, after: cursor }),\n graphUrl(client, `/${id}/client_pages`, { fields: pageFallbackFields, limit, after: cursor }),\n warnings,\n \"Client Page access may require Business Manager partner permissions.\",\n ),\n fetchGraph(\n client,\n `business:${id}:owned_instagram_accounts`,\n graphUrl(client, `/${id}/owned_instagram_accounts`, { fields: instagramFields, limit, after: cursor }),\n warnings,\n \"Grant business_management and instagram_basic to read owned Instagram accounts.\",\n ),\n fetchGraph(\n client,\n `business:${id}:client_instagram_accounts`,\n graphUrl(client, `/${id}/client_instagram_accounts`, { fields: instagramFields, limit, after: cursor }),\n warnings,\n \"Client Instagram access may require Business Manager partner permissions and instagram_basic.\",\n ),\n fetchGraphWithFallback(\n client,\n `business:${id}:owned_pixels`,\n graphUrl(client, `/${id}/owned_pixels`, { fields: pixelFields, limit, after: cursor }),\n graphUrl(client, `/${id}/owned_pixels`, { fields: pixelFallbackFields, limit, after: cursor }),\n warnings,\n \"Grant business_management and asset access to read owned pixels.\",\n ),\n fetchGraphWithFallback(\n client,\n `business:${id}:client_pixels`,\n graphUrl(client, `/${id}/client_pixels`, { fields: pixelFields, limit, after: cursor }),\n graphUrl(client, `/${id}/client_pixels`, { fields: pixelFallbackFields, limit, after: cursor }),\n warnings,\n \"Client pixel access may require Business Manager partner permissions.\",\n ),\n fetchGraphWithFallback(\n client,\n `business:${id}:owned_data_sets`,\n graphUrl(client, `/${id}/owned_data_sets`, { fields: datasetFields, limit, after: cursor }),\n graphUrl(client, `/${id}/owned_data_sets`, { fields: datasetFallbackFields, limit, after: cursor }),\n warnings,\n \"Dataset edges vary by Meta account setup; try adAccountId fallback or verify business asset permissions.\",\n ),\n fetchGraphWithFallback(\n client,\n `business:${id}:client_data_sets`,\n graphUrl(client, `/${id}/client_data_sets`, { fields: datasetFields, limit, after: cursor }),\n graphUrl(client, `/${id}/client_data_sets`, { fields: datasetFallbackFields, limit, after: cursor }),\n warnings,\n \"Client dataset access may require partner permissions or may not be available for this business.\",\n ),\n ]);\n\n addById(pagesById, dataArray(ownedPages).map((page) => normalizePage(page, `business:${id}:owned_pages`)));\n addById(pagesById, dataArray(clientPages).map((page) => normalizePage(page, `business:${id}:client_pages`)));\n\n for (const page of [...dataArray(ownedPages), ...dataArray(clientPages)]) {\n const pageContext = {\n page_id: page.id,\n page_name: page.name,\n business_id: id,\n };\n if (isRecord(page.instagram_business_account)) {\n addById(instagramById, [\n normalizeInstagramAccount(page.instagram_business_account, \"page.instagram_business_account\", pageContext),\n ]);\n }\n if (isRecord(page.connected_instagram_account)) {\n addById(instagramById, [\n normalizeInstagramAccount(page.connected_instagram_account, \"page.connected_instagram_account\", pageContext),\n ]);\n }\n }\n\n addById(instagramById, dataArray(ownedInstagram).map((account) => normalizeInstagramAccount(account, `business:${id}:owned_instagram_accounts`, { business_id: id })));\n addById(instagramById, dataArray(clientInstagram).map((account) => normalizeInstagramAccount(account, `business:${id}:client_instagram_accounts`, { business_id: id })));\n addById(pixelsById, dataArray(ownedPixels));\n addById(pixelsById, dataArray(clientPixels));\n addById(datasetsById, dataArray(ownedDatasets));\n addById(datasetsById, dataArray(clientDatasets));\n }\n\n if (businessIds.size === 0) {\n warnings.push({\n area: \"business_assets\",\n message: \"No readable Business Manager was discovered from /me/businesses or the provided ad account.\",\n suggestion: \"Provide businessId directly, grant business_management, or use adAccountId for account-level pixels/datasets.\",\n });\n }\n\n return ok({\n businesses: [...businessesById.values()],\n pages: [...pagesById.values()],\n instagram_accounts: [...instagramById.values()],\n pixels: [...pixelsById.values()],\n datasets: [...datasetsById.values()],\n counts: {\n businesses: businessesById.size,\n pages: pagesById.size,\n instagram_accounts: instagramById.size,\n pixels: pixelsById.size,\n datasets: datasetsById.size,\n },\n warnings,\n });\n },\n );\n\n // ── P0/P1. meta_get_pages ─────────────────────────────────────────\n server.tool(\n \"meta_get_pages\",\n \"List accessible Facebook Pages with id, name, category, tasks, picture, and linked Instagram account references when available.\",\n {\n businessId: z.string().optional().describe(\"Optional Business Manager ID to list owned/client pages. Omit to use /me/accounts.\"),\n limit: z.number().int().min(1).max(200).optional().default(100),\n cursor: cursorSchema,\n },\n async ({ businessId, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const pagesById = new Map<string, Record<string, unknown>>();\n const fields = \"id,name,category,tasks,picture{url},instagram_business_account{id,username,name,ig_id,profile_picture_url},connected_instagram_account{id,username,name,ig_id,profile_picture_url}\";\n const fallbackFields = \"id,name,category,tasks,picture{url}\";\n const paging: Record<string, unknown> = {};\n\n if (businessId) {\n const ownedPages = await fetchGraphWithFallback(\n client,\n \"owned_pages\",\n graphUrl(client, `/${businessId}/owned_pages`, { fields, limit, after: cursor }),\n graphUrl(client, `/${businessId}/owned_pages`, { fields: fallbackFields, limit, after: cursor }),\n warnings,\n \"Grant business_management plus pages_show_list/pages_read_engagement to read Business-owned Pages.\",\n );\n const clientPages = await fetchGraphWithFallback(\n client,\n \"client_pages\",\n graphUrl(client, `/${businessId}/client_pages`, { fields, limit, after: cursor }),\n graphUrl(client, `/${businessId}/client_pages`, { fields: fallbackFields, limit, after: cursor }),\n warnings,\n \"Client Page access may require Business Manager partner permissions.\",\n );\n\n addById(pagesById, dataArray(ownedPages).map((page) => normalizePage(page, \"owned_pages\")));\n addById(pagesById, dataArray(clientPages).map((page) => normalizePage(page, \"client_pages\")));\n paging.owned_pages = pagingInfo(ownedPages);\n paging.client_pages = pagingInfo(clientPages);\n } else {\n const pages = await fetchGraphWithFallback(\n client,\n \"me_accounts\",\n graphUrl(client, \"/me/accounts\", { fields, limit, after: cursor }),\n graphUrl(client, \"/me/accounts\", { fields: fallbackFields, limit, after: cursor }),\n warnings,\n \"Grant pages_show_list and pages_read_engagement to list Pages managed by the user.\",\n );\n addById(pagesById, dataArray(pages).map((page) => normalizePage(page, \"me/accounts\")));\n paging.me_accounts = pagingInfo(pages);\n }\n\n return ok({\n pages: [...pagesById.values()],\n count: pagesById.size,\n paging,\n warnings,\n });\n },\n );\n\n // ── P0/P1. meta_get_instagram_accounts ────────────────────────────\n server.tool(\n \"meta_get_instagram_accounts\",\n \"List Instagram accounts linked to accessible Pages, Business Manager assets, or an ad account when permissions allow.\",\n {\n adAccountId: adAccountIdSchema.optional().describe(\"Optional ad account ID for /instagram_accounts.\"),\n businessId: z.string().optional().describe(\"Optional Business Manager ID for owned/client Instagram account edges.\"),\n pageId: z.string().optional().describe(\"Optional Page ID to read linked instagram_business_account/connected_instagram_account.\"),\n limit: z.number().int().min(1).max(200).optional().default(100),\n cursor: cursorSchema,\n },\n async ({ adAccountId, businessId, pageId, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const accountsById = new Map<string, Record<string, unknown>>();\n const fields = \"id,username,name,ig_id,profile_picture_url\";\n const pageFields = \"id,name,instagram_business_account{id,username,name,ig_id,profile_picture_url},connected_instagram_account{id,username,name,ig_id,profile_picture_url}\";\n const paging: Record<string, unknown> = {};\n\n if (pageId) {\n const page = await fetchGraph(\n client,\n \"page_instagram_accounts\",\n graphUrl(client, `/${pageId}`, { fields: pageFields }),\n warnings,\n \"Grant pages_show_list/pages_read_engagement and ensure the Instagram account is linked to this Page.\",\n );\n const context = { page_id: page?.id ?? pageId, page_name: page?.name };\n if (isRecord(page?.instagram_business_account)) {\n addById(accountsById, [\n normalizeInstagramAccount(page.instagram_business_account, \"page.instagram_business_account\", context),\n ]);\n }\n if (isRecord(page?.connected_instagram_account)) {\n addById(accountsById, [\n normalizeInstagramAccount(page.connected_instagram_account, \"page.connected_instagram_account\", context),\n ]);\n }\n }\n\n if (adAccountId) {\n const adAccountAccounts = await fetchGraph(\n client,\n \"ad_account_instagram_accounts\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/instagram_accounts`, {\n fields,\n limit,\n after: cursor,\n }),\n warnings,\n \"Grant ads_read and instagram_basic, and confirm the IG account is available to the ad account.\",\n );\n addById(accountsById, dataArray(adAccountAccounts).map((account) => normalizeInstagramAccount(account, \"ad_account.instagram_accounts\", { ad_account_id: formatAdAccountId(adAccountId) })));\n paging.ad_account_instagram_accounts = pagingInfo(adAccountAccounts);\n }\n\n if (businessId) {\n const owned = await fetchGraph(\n client,\n \"owned_instagram_accounts\",\n graphUrl(client, `/${businessId}/owned_instagram_accounts`, { fields, limit, after: cursor }),\n warnings,\n \"Grant business_management and instagram_basic to read owned Instagram accounts.\",\n );\n const clientAccounts = await fetchGraph(\n client,\n \"client_instagram_accounts\",\n graphUrl(client, `/${businessId}/client_instagram_accounts`, { fields, limit, after: cursor }),\n warnings,\n \"Client Instagram account access may require partner permissions and instagram_basic.\",\n );\n addById(accountsById, dataArray(owned).map((account) => normalizeInstagramAccount(account, \"business.owned_instagram_accounts\", { business_id: businessId })));\n addById(accountsById, dataArray(clientAccounts).map((account) => normalizeInstagramAccount(account, \"business.client_instagram_accounts\", { business_id: businessId })));\n paging.owned_instagram_accounts = pagingInfo(owned);\n paging.client_instagram_accounts = pagingInfo(clientAccounts);\n }\n\n if (!pageId && !adAccountId && !businessId) {\n const pages = await fetchGraph(\n client,\n \"me_accounts_instagram\",\n graphUrl(client, \"/me/accounts\", {\n fields: \"id,name,instagram_business_account{id,username,name,ig_id,profile_picture_url},connected_instagram_account{id,username,name,ig_id,profile_picture_url}\",\n limit,\n after: cursor,\n }),\n warnings,\n \"Grant pages_show_list/pages_read_engagement and instagram_basic to discover IG accounts via Pages.\",\n );\n for (const page of dataArray(pages)) {\n const context = { page_id: page.id, page_name: page.name };\n if (isRecord(page.instagram_business_account)) {\n addById(accountsById, [\n normalizeInstagramAccount(page.instagram_business_account, \"me/accounts.instagram_business_account\", context),\n ]);\n }\n if (isRecord(page.connected_instagram_account)) {\n addById(accountsById, [\n normalizeInstagramAccount(page.connected_instagram_account, \"me/accounts.connected_instagram_account\", context),\n ]);\n }\n }\n paging.me_accounts = pagingInfo(pages);\n }\n\n return ok({\n instagram_accounts: [...accountsById.values()],\n count: accountsById.size,\n paging,\n warnings,\n });\n },\n );\n\n // ── P0/P1. meta_get_pixels ────────────────────────────────────────\n server.tool(\n \"meta_get_pixels\",\n \"List pixels and datasets from an ad account or Business Manager when accessible, returning actionable warnings for permission-limited edges.\",\n {\n adAccountId: adAccountIdSchema.optional().describe(\"Ad account ID for /adspixels and offline conversion datasets.\"),\n businessId: z.string().optional().describe(\"Business Manager ID for owned/client pixels and datasets.\"),\n includeDatasets: z.boolean().optional().default(true).describe(\"Also attempt dataset/offline conversion dataset edges.\"),\n limit: z.number().int().min(1).max(200).optional().default(100),\n cursor: cursorSchema,\n },\n async ({ adAccountId, businessId, includeDatasets, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const pixelsById = new Map<string, Record<string, unknown>>();\n const datasetsById = new Map<string, Record<string, unknown>>();\n const pixelFields = \"id,name,last_fired_time,creation_time,owner_ad_account,business\";\n const pixelFallbackFields = \"id,name,last_fired_time,creation_time\";\n const datasetFields = \"id,name,description,creation_time,updated_time\";\n const datasetFallbackFields = \"id,name\";\n const paging: Record<string, unknown> = {};\n\n if (!adAccountId && !businessId) {\n warnings.push({\n area: \"pixels\",\n message: \"No adAccountId or businessId was provided.\",\n suggestion: \"Pass adAccountId for account pixels, or businessId for Business Manager-owned/client pixels.\",\n });\n }\n\n if (adAccountId) {\n const pixels = await fetchGraphWithFallback(\n client,\n \"ad_account_pixels\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/adspixels`, {\n fields: pixelFields,\n limit,\n after: cursor,\n }),\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/adspixels`, {\n fields: pixelFallbackFields,\n limit,\n after: cursor,\n }),\n warnings,\n \"Grant ads_read and ensure the token has access to the ad account's pixel assets.\",\n );\n addById(pixelsById, dataArray(pixels));\n paging.ad_account_pixels = pagingInfo(pixels);\n\n if (includeDatasets) {\n const datasets = await fetchGraphWithFallback(\n client,\n \"ad_account_offline_conversion_data_sets\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/offline_conversion_data_sets`, {\n fields: datasetFields,\n limit,\n after: cursor,\n }),\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/offline_conversion_data_sets`, {\n fields: datasetFallbackFields,\n limit,\n after: cursor,\n }),\n warnings,\n \"Offline datasets may require business-level asset access; try businessId if this edge is unavailable.\",\n );\n addById(datasetsById, dataArray(datasets));\n paging.ad_account_offline_conversion_data_sets = pagingInfo(datasets);\n }\n }\n\n if (businessId) {\n const [ownedPixels, clientPixels] = await Promise.all([\n fetchGraphWithFallback(\n client,\n \"business_owned_pixels\",\n graphUrl(client, `/${businessId}/owned_pixels`, { fields: pixelFields, limit, after: cursor }),\n graphUrl(client, `/${businessId}/owned_pixels`, { fields: pixelFallbackFields, limit, after: cursor }),\n warnings,\n \"Grant business_management and pixel asset permissions to read Business-owned pixels.\",\n ),\n fetchGraphWithFallback(\n client,\n \"business_client_pixels\",\n graphUrl(client, `/${businessId}/client_pixels`, { fields: pixelFields, limit, after: cursor }),\n graphUrl(client, `/${businessId}/client_pixels`, { fields: pixelFallbackFields, limit, after: cursor }),\n warnings,\n \"Client pixel access may require partner permissions in Business Manager.\",\n ),\n ]);\n addById(pixelsById, dataArray(ownedPixels));\n addById(pixelsById, dataArray(clientPixels));\n paging.business_owned_pixels = pagingInfo(ownedPixels);\n paging.business_client_pixels = pagingInfo(clientPixels);\n\n if (includeDatasets) {\n const [ownedDatasets, clientDatasets] = await Promise.all([\n fetchGraphWithFallback(\n client,\n \"business_owned_data_sets\",\n graphUrl(client, `/${businessId}/owned_data_sets`, { fields: datasetFields, limit, after: cursor }),\n graphUrl(client, `/${businessId}/owned_data_sets`, { fields: datasetFallbackFields, limit, after: cursor }),\n warnings,\n \"Dataset edges vary by Meta account setup; verify Business Manager asset permissions.\",\n ),\n fetchGraphWithFallback(\n client,\n \"business_client_data_sets\",\n graphUrl(client, `/${businessId}/client_data_sets`, { fields: datasetFields, limit, after: cursor }),\n graphUrl(client, `/${businessId}/client_data_sets`, { fields: datasetFallbackFields, limit, after: cursor }),\n warnings,\n \"Client dataset access may require partner permissions or may not be available for this business.\",\n ),\n ]);\n addById(datasetsById, dataArray(ownedDatasets));\n addById(datasetsById, dataArray(clientDatasets));\n paging.business_owned_data_sets = pagingInfo(ownedDatasets);\n paging.business_client_data_sets = pagingInfo(clientDatasets);\n }\n }\n\n return ok({\n pixels: [...pixelsById.values()],\n datasets: [...datasetsById.values()],\n counts: {\n pixels: pixelsById.size,\n datasets: datasetsById.size,\n },\n paging,\n warnings,\n });\n },\n );\n\n // ── P0/P1. meta_get_ad_activity ───────────────────────────────────\n server.tool(\n \"meta_get_ad_activity\",\n \"Read ad account activity logs from /{ad_account_id}/activities with object, event, actor, timestamp, and extra_data fields.\",\n {\n adAccountId: adAccountIdSchema,\n since: z.string().optional().describe(\"Optional start time accepted by Graph API, usually YYYY-MM-DD or Unix seconds.\"),\n until: z.string().optional().describe(\"Optional end time accepted by Graph API, usually YYYY-MM-DD or Unix seconds.\"),\n limit: z.number().int().min(1).max(500).optional().default(100),\n cursor: cursorSchema,\n },\n async ({ adAccountId, since, until, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const fields = \"object_id,object_name,event_type,translated_event_type,event_time,actor_id,actor_name,extra_data\";\n const result = await fetchGraph(\n client,\n \"ad_activity\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/activities`, {\n fields,\n since,\n until,\n limit,\n after: cursor,\n }),\n warnings,\n \"Grant ads_read and confirm the token user can view activity history for this ad account.\",\n );\n const activities = dataArray(result).map((activity) => ({\n ...activity,\n actor: {\n id: activity.actor_id,\n name: activity.actor_name,\n },\n }));\n\n return ok({\n activities,\n count: activities.length,\n paging: pagingInfo(result),\n warnings,\n });\n },\n );\n\n // ── P0/P1. meta_get_delivery_diagnostics ──────────────────────────\n server.tool(\n \"meta_get_delivery_diagnostics\",\n \"Aggregate read-only delivery diagnostics across campaigns, ad sets, and ads using status/effective_status/issues_info where available plus simple delivery insights.\",\n {\n adAccountId: adAccountIdSchema,\n level: z.enum([\"campaign\", \"adset\", \"ad\", \"all\"]).optional().default(\"all\"),\n effectiveStatusFilter: z.array(z.string()).optional().describe(\"Optional effective_status filter values such as ACTIVE, PAUSED, WITH_ISSUES, DISAPPROVED.\"),\n datePreset: datePresetSchema.describe(\"Insights date preset for simple delivery metrics. Defaults to last_7d.\"),\n timeRange: timeRangeSchema.describe(\"Optional custom insights date range.\"),\n limit: z.number().int().min(1).max(200).optional().default(100),\n cursor: cursorSchema,\n },\n async ({ adAccountId, level, effectiveStatusFilter, datePreset, timeRange, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const diagnostics: Record<string, unknown> = {};\n const simpleInsights: Record<string, unknown> = {};\n const paging: Record<string, unknown> = {};\n const selectedLevels = level === \"all\" ? [\"campaign\", \"adset\", \"ad\"] as const : [level] as const;\n const edges = {\n campaign: \"campaigns\",\n adset: \"adsets\",\n ad: \"ads\",\n } as const;\n const richFields = {\n campaign: \"id,name,status,effective_status,configured_status,objective,buying_type,issues_info,start_time,stop_time,created_time,updated_time\",\n adset: \"id,name,status,effective_status,configured_status,campaign_id,optimization_goal,billing_event,issues_info,learning_stage_info,start_time,end_time,daily_budget,lifetime_budget,created_time,updated_time\",\n ad: \"id,name,status,effective_status,configured_status,adset_id,campaign_id,issues_info,creative{id,name,thumbnail_url},created_time,updated_time\",\n } as const;\n const fallbackFields = {\n campaign: \"id,name,status,effective_status,objective,start_time,stop_time\",\n adset: \"id,name,status,effective_status,campaign_id,optimization_goal,start_time,end_time\",\n ad: \"id,name,status,effective_status,adset_id,campaign_id,creative{id,name}\",\n } as const;\n const insightFields = {\n campaign: [\"campaign_id\", \"campaign_name\", \"impressions\", \"reach\", \"spend\", \"clicks\", \"ctr\"],\n adset: [\"campaign_id\", \"campaign_name\", \"adset_id\", \"adset_name\", \"impressions\", \"reach\", \"spend\", \"clicks\", \"ctr\"],\n ad: [\"campaign_id\", \"campaign_name\", \"adset_id\", \"adset_name\", \"ad_id\", \"ad_name\", \"impressions\", \"reach\", \"spend\", \"clicks\", \"ctr\"],\n } as const;\n\n if (level === \"all\" && cursor) {\n warnings.push({\n area: \"pagination\",\n message: \"A single cursor was provided while level=all.\",\n suggestion: \"Use a specific level with the returned paging cursor for precise pagination.\",\n });\n }\n\n for (const entityLevel of selectedLevels) {\n const filters = effectiveStatusFilter?.length\n ? JSON.stringify([{ field: \"effective_status\", operator: \"IN\", value: effectiveStatusFilter }])\n : undefined;\n const result = await fetchGraphWithFallback(\n client,\n `delivery_${entityLevel}`,\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/${edges[entityLevel]}`, {\n fields: richFields[entityLevel],\n filtering: filters,\n limit,\n after: cursor,\n }),\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/${edges[entityLevel]}`, {\n fields: fallbackFields[entityLevel],\n filtering: filters,\n limit,\n after: cursor,\n }),\n warnings,\n \"Some diagnostic fields are permission/version dependent; minimal status fields are returned when rich fields are unavailable.\",\n );\n\n diagnostics[`${edges[entityLevel]}`] = dataArray(result).map(entityIssueSummary);\n paging[edges[entityLevel]] = pagingInfo(result);\n\n try {\n simpleInsights[entityLevel] = await client.fetchInsights(formatAdAccountId(adAccountId), {\n type: \"delivery_diagnostics\",\n fields: [...insightFields[entityLevel]],\n level: entityLevel,\n limit,\n datePreset: timeRange ? undefined : (datePreset ?? \"last_7d\"),\n timeRange,\n });\n } catch (error) {\n warnings.push(warningFromError(\n `delivery_${entityLevel}_insights`,\n error,\n \"Simple delivery insights are optional; verify ads_read and metric availability for this account.\",\n ));\n }\n }\n\n return ok({\n ad_account_id: formatAdAccountId(adAccountId),\n date_range: timeRange ?? { date_preset: datePreset ?? \"last_7d\" },\n diagnostics,\n simple_insights: simpleInsights,\n paging,\n warnings: [\n {\n area: \"delivery_diagnostics\",\n message: \"Meta does not expose one stable read-only delivery diagnostics endpoint for every issue type.\",\n suggestion: \"Use effective_status, issues_info when present, learning_stage_info when present, and insights together to triage delivery.\",\n },\n ...warnings,\n ],\n });\n },\n );\n\n // ── P0/P1. meta_get_creative_assets ───────────────────────────────\n server.tool(\n \"meta_get_creative_assets\",\n \"Return normalized creative asset metadata for ads or creative IDs: thumbnail, object_story_spec, asset_feed_spec, video/image/link URL, CTA, and page/IG references.\",\n {\n adAccountId: adAccountIdSchema.optional().describe(\"Ad account ID. Used when adIds/creativeIds are omitted.\"),\n adIds: z.array(z.string()).optional().describe(\"Specific ad IDs to enrich.\"),\n creativeIds: z.array(z.string()).optional().describe(\"Specific creative IDs to enrich.\"),\n limit: limitSchema,\n cursor: cursorSchema,\n },\n async ({ adAccountId, adIds, creativeIds, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const creativeFields = \"id,name,body,title,thumbnail_url,image_url,video_id,link_url,call_to_action_type,effective_object_story_id,object_type,object_story_spec,asset_feed_spec\";\n const adFields = `id,name,status,effective_status,campaign_id,adset_id,creative{${creativeFields}}`;\n let sourceType: \"ad\" | \"creative\" = \"ad\";\n let result: Record<string, unknown> | null = null;\n const idChunkSize = 50;\n\n if (creativeIds && creativeIds.length > 0) {\n sourceType = \"creative\";\n const merged: Record<string, unknown> = {};\n for (let index = 0; index < creativeIds.length; index += idChunkSize) {\n const chunk = creativeIds.slice(index, index + idChunkSize);\n const chunkResult = await fetchGraph(\n client,\n `creative_ids_${Math.floor(index / idChunkSize) + 1}`,\n graphUrl(client, \"\", { ids: chunk.join(\",\"), fields: creativeFields }),\n warnings,\n \"Verify the creative IDs are readable by this token and include only read-only creative fields.\",\n );\n Object.assign(merged, chunkResult ?? {});\n }\n result = merged;\n if (creativeIds.length > idChunkSize) {\n warnings.push({\n area: \"creative_assets\",\n message: `Creative ID enrichment was chunked into batches of ${idChunkSize} to avoid oversized Graph API responses.`,\n suggestion: \"Prefer passing targeted creativeIds/adIds from insights for complete creative-to-site coverage.\",\n });\n }\n } else if (adIds && adIds.length > 0) {\n sourceType = \"ad\";\n const merged: Record<string, unknown> = {};\n for (let index = 0; index < adIds.length; index += idChunkSize) {\n const chunk = adIds.slice(index, index + idChunkSize);\n const chunkResult = await fetchGraph(\n client,\n `ad_ids_creatives_${Math.floor(index / idChunkSize) + 1}`,\n graphUrl(client, \"\", { ids: chunk.join(\",\"), fields: adFields }),\n warnings,\n \"Verify the ad IDs are readable by this token and belong to accessible ad accounts.\",\n );\n Object.assign(merged, chunkResult ?? {});\n }\n result = merged;\n if (adIds.length > idChunkSize) {\n warnings.push({\n area: \"creative_assets\",\n message: `Ad ID creative enrichment was chunked into batches of ${idChunkSize} to avoid oversized Graph API responses.`,\n suggestion: \"Pass adIds from the spend-ranked insights rows to cover all spend-bearing ads without broad account paging.\",\n });\n }\n } else if (adAccountId) {\n sourceType = \"ad\";\n result = await fetchGraph(\n client,\n \"ad_account_creative_assets\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/ads`, {\n fields: adFields,\n limit,\n after: cursor,\n }),\n warnings,\n \"Pass adIds/creativeIds directly or grant ads_read for the ad account.\",\n );\n } else {\n warnings.push({\n area: \"creative_assets\",\n message: \"No adAccountId, adIds, or creativeIds were provided.\",\n suggestion: \"Pass adAccountId to list ad creatives, or pass adIds/creativeIds for targeted enrichment.\",\n });\n }\n\n const records = result?.data\n ? dataArray(result)\n : Object.values(result ?? {}).filter(isRecord);\n const assets = records.map((record) => normalizeCreativeAsset(record, sourceType));\n const paging = pagingInfo(result);\n if (paging) {\n warnings.push({\n area: \"creative_assets\",\n message: \"Creative assets response is paginated.\",\n suggestion: \"Use the returned paging cursor for the next page, or pass explicit adIds from insights for deterministic creative-to-site coverage.\",\n });\n }\n\n return ok({\n assets,\n count: assets.length,\n paging,\n warnings,\n });\n },\n );\n\n // ── P0/P1. meta_get_audience_details ──────────────────────────────\n server.tool(\n \"meta_get_audience_details\",\n \"Read detailed custom, saved, and lookalike audiences with pagination and rich fields where permissions allow.\",\n {\n adAccountId: adAccountIdSchema.optional().describe(\"Ad account ID used when listing audiences.\"),\n audienceIds: z.array(z.string()).optional().describe(\"Specific audience IDs to fetch via batch IDs lookup.\"),\n type: z.enum([\"all\", \"custom\", \"saved\", \"lookalike\"]).optional().default(\"all\"),\n limit: limitSchema,\n cursor: cursorSchema,\n },\n async ({ adAccountId, audienceIds, type, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const customAudiences: Record<string, unknown>[] = [];\n const savedAudiences: Record<string, unknown>[] = [];\n const lookalikeAudiences: Record<string, unknown>[] = [];\n const paging: Record<string, unknown> = {};\n const customFields = [\n \"id\",\n \"name\",\n \"description\",\n \"subtype\",\n \"approximate_count_lower_bound\",\n \"approximate_count_upper_bound\",\n \"delivery_status\",\n \"operation_status\",\n \"permission_for_actions\",\n \"lookalike_spec\",\n \"lookalike_audience_ids\",\n \"customer_file_source\",\n \"retention_days\",\n \"time_created\",\n \"time_updated\",\n \"data_source\",\n \"rule\",\n \"account_id\",\n ].join(\",\");\n const customFallbackFields = \"id,name,description,subtype,delivery_status,time_created,time_updated\";\n const savedFields = \"id,name,description,targeting,account_id,time_created,time_updated,run_status\";\n const savedFallbackFields = \"id,name,description,targeting\";\n\n if (audienceIds && audienceIds.length > 0) {\n const fields = type === \"saved\" ? savedFields : customFields;\n const fallbackFields = type === \"saved\" ? savedFallbackFields : customFallbackFields;\n const result = await fetchGraphWithFallback(\n client,\n \"audience_ids\",\n graphUrl(client, \"\", { ids: audienceIds.join(\",\"), fields }),\n graphUrl(client, \"\", { ids: audienceIds.join(\",\"), fields: fallbackFields }),\n warnings,\n \"Some audience detail fields are restricted; minimal audience fields are returned when rich fields fail.\",\n );\n for (const audience of Object.values(result ?? {}).filter(isRecord)) {\n if (audience.subtype === \"LOOKALIKE\") {\n lookalikeAudiences.push(audience);\n } else if (type === \"saved\" || audience.targeting) {\n savedAudiences.push(audience);\n } else {\n customAudiences.push(audience);\n }\n }\n } else if (!adAccountId) {\n warnings.push({\n area: \"audiences\",\n message: \"No adAccountId or audienceIds were provided.\",\n suggestion: \"Pass adAccountId to list audiences, or audienceIds for targeted details.\",\n });\n } else {\n if (type === \"all\" && cursor) {\n warnings.push({\n area: \"pagination\",\n message: \"A single cursor was provided while type=all.\",\n suggestion: \"Use type=custom, type=lookalike, or type=saved with the returned cursor for precise pagination.\",\n });\n }\n\n if (type === \"all\" || type === \"custom\" || type === \"lookalike\") {\n const filtering = type === \"lookalike\"\n ? JSON.stringify([{ field: \"subtype\", operator: \"EQUAL\", value: \"LOOKALIKE\" }])\n : undefined;\n const result = await fetchGraphWithFallback(\n client,\n \"customaudiences\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/customaudiences`, {\n fields: customFields,\n filtering,\n limit,\n after: cursor,\n }),\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/customaudiences`, {\n fields: customFallbackFields,\n filtering,\n limit,\n after: cursor,\n }),\n warnings,\n \"Grant ads_read and audience asset access. Some audience detail fields are restricted by Meta privacy rules.\",\n );\n for (const audience of dataArray(result)) {\n if (audience.subtype === \"LOOKALIKE\") {\n lookalikeAudiences.push(audience);\n } else {\n customAudiences.push(audience);\n }\n }\n paging.customaudiences = pagingInfo(result);\n }\n\n if (type === \"all\" || type === \"saved\") {\n const result = await fetchGraphWithFallback(\n client,\n \"saved_audiences\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/saved_audiences`, {\n fields: savedFields,\n limit,\n after: cursor,\n }),\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/saved_audiences`, {\n fields: savedFallbackFields,\n limit,\n after: cursor,\n }),\n warnings,\n \"Grant ads_read and ensure the token user can read saved audiences for this account.\",\n );\n savedAudiences.push(...dataArray(result));\n paging.saved_audiences = pagingInfo(result);\n }\n }\n\n return ok({\n custom_audiences: customAudiences,\n saved_audiences: savedAudiences,\n lookalike_audiences: lookalikeAudiences,\n counts: {\n custom_audiences: customAudiences.length,\n saved_audiences: savedAudiences.length,\n lookalike_audiences: lookalikeAudiences.length,\n },\n paging,\n warnings,\n });\n },\n );\n\n // ── P2. meta_get_catalog_products ────────────────────────────────\n server.tool(\n \"meta_get_catalog_products\",\n \"Read Product Catalogs and Product Items when catalog access is available. Returns join-ready product metadata without creating or updating catalog assets.\",\n {\n businessId: z.string().optional().describe(\"Business Manager ID used to discover owned/client product catalogs.\"),\n catalogId: z.string().optional().describe(\"Product Catalog ID to read directly.\"),\n productIds: z.array(z.string()).max(100).optional().describe(\"Specific Product Item IDs to fetch via the batch IDs endpoint.\"),\n search: z.string().optional().describe(\"Client-side substring filter across product id, retailer_id, name, brand, category, type, and custom labels.\"),\n includeProducts: z.boolean().optional().default(true).describe(\"Fetch product items for discovered/provided catalogs.\"),\n includeProductSets: z.boolean().optional().default(false).describe(\"Also attempt product_sets edges for catalog context.\"),\n limit: z.number().int().min(1).max(500).optional().default(100),\n cursor: cursorSchema,\n },\n async ({ businessId, catalogId, productIds, search, includeProducts, includeProductSets, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const catalogsById = new Map<string, Record<string, unknown>>();\n const productsById = new Map<string, Record<string, unknown>>();\n const productSetsById = new Map<string, Record<string, unknown>>();\n const paging: Record<string, unknown> = {};\n const catalogFields = \"id,name,vertical,product_count,feed_count,business,default_image_url,fallback_image_url\";\n const catalogFallbackFields = \"id,name,vertical,product_count\";\n const productFields = [\n \"id\",\n \"retailer_id\",\n \"name\",\n \"description\",\n \"brand\",\n \"category\",\n \"fb_product_category\",\n \"product_type\",\n \"price\",\n \"sale_price\",\n \"currency\",\n \"availability\",\n \"inventory\",\n \"visibility\",\n \"review_status\",\n \"url\",\n \"image_url\",\n \"additional_image_urls\",\n \"custom_label_0\",\n \"custom_label_1\",\n \"custom_label_2\",\n \"custom_label_3\",\n \"custom_label_4\",\n \"retailer_product_group_id\",\n \"product_catalog{id,name}\",\n \"product_group{id}\",\n ].join(\",\");\n const productFallbackFields = \"id,retailer_id,name,brand,price,availability,url,image_url\";\n const productSetFields = \"id,name,filter,product_count\";\n\n if (!businessId && !catalogId && (!productIds || productIds.length === 0)) {\n warnings.push({\n area: \"catalog_products\",\n message: \"No businessId, catalogId, or productIds were provided.\",\n suggestion: \"Pass catalogId for a direct read, businessId to discover catalogs, or productIds for targeted enrichment.\",\n });\n }\n\n if (productIds && productIds.length > 0) {\n const products = await fetchGraphWithFallback(\n client,\n \"catalog_product_ids\",\n graphUrl(client, \"\", { ids: productIds.join(\",\"), fields: productFields }),\n graphUrl(client, \"\", { ids: productIds.join(\",\"), fields: productFallbackFields }),\n warnings,\n \"Product Item reads may require catalog asset access. Verify catalog permissions if rich fields are unavailable.\",\n );\n for (const product of Object.values(products ?? {}).filter(isRecord)) {\n const normalized = normalizeProductItem(product, \"ids\");\n if (productSearchMatches(normalized, search)) {\n productsById.set(String(normalized.id ?? normalized.retailer_id), normalized);\n }\n }\n }\n\n if (catalogId) {\n const catalog = await fetchGraphWithFallback(\n client,\n \"catalog\",\n graphUrl(client, `/${catalogId}`, { fields: catalogFields }),\n graphUrl(client, `/${catalogId}`, { fields: catalogFallbackFields }),\n warnings,\n \"Verify the token has access to this Product Catalog. Some catalog fields are permission/version dependent.\",\n );\n if (catalog) catalogsById.set(String(catalog.id ?? catalogId), catalog);\n }\n\n if (businessId) {\n const [ownedCatalogs, clientCatalogs] = await Promise.all([\n fetchGraphWithFallback(\n client,\n \"business_owned_product_catalogs\",\n graphUrl(client, `/${businessId}/owned_product_catalogs`, { fields: catalogFields, limit, after: cursor }),\n graphUrl(client, `/${businessId}/owned_product_catalogs`, { fields: catalogFallbackFields, limit, after: cursor }),\n warnings,\n \"Grant business_management and catalog asset access to read Business-owned product catalogs.\",\n ),\n fetchGraphWithFallback(\n client,\n \"business_client_product_catalogs\",\n graphUrl(client, `/${businessId}/client_product_catalogs`, { fields: catalogFields, limit, after: cursor }),\n graphUrl(client, `/${businessId}/client_product_catalogs`, { fields: catalogFallbackFields, limit, after: cursor }),\n warnings,\n \"Client catalog access may require partner permissions or a catalog-capable token.\",\n ),\n ]);\n addById(catalogsById, dataArray(ownedCatalogs));\n addById(catalogsById, dataArray(clientCatalogs));\n paging.business_owned_product_catalogs = pagingInfo(ownedCatalogs);\n paging.business_client_product_catalogs = pagingInfo(clientCatalogs);\n }\n\n const catalogIds = [...catalogsById.keys()];\n if (includeProducts) {\n if (catalogIds.length > 1 && cursor) {\n warnings.push({\n area: \"catalog_products_pagination\",\n message: \"A single cursor was provided while multiple catalogs are being read.\",\n suggestion: \"Use catalogId with the returned catalog-specific cursor for precise product pagination.\",\n });\n }\n\n for (const id of catalogIds) {\n const products = await fetchGraphWithFallback(\n client,\n `catalog:${id}:products`,\n graphUrl(client, `/${id}/products`, { fields: productFields, limit, after: cursor }),\n graphUrl(client, `/${id}/products`, { fields: productFallbackFields, limit, after: cursor }),\n warnings,\n \"Product Item fields may require catalog permissions. Minimal join fields are returned when rich product fields fail.\",\n );\n const normalizedProducts = dataArray(products)\n .map((product) => normalizeProductItem(product, `catalog:${id}:products`))\n .filter((product) => productSearchMatches(product, search));\n addById(productsById, normalizedProducts);\n paging[`catalog:${id}:products`] = pagingInfo(products);\n }\n }\n\n if (includeProductSets) {\n for (const id of catalogIds) {\n const productSets = await fetchGraph(\n client,\n `catalog:${id}:product_sets`,\n graphUrl(client, `/${id}/product_sets`, { fields: productSetFields, limit, after: cursor }),\n warnings,\n \"Product set reads may require catalog access, and some catalog verticals do not expose product_sets.\",\n );\n addById(productSetsById, dataArray(productSets));\n paging[`catalog:${id}:product_sets`] = pagingInfo(productSets);\n }\n }\n\n return ok({\n catalogs: [...catalogsById.values()],\n products: [...productsById.values()],\n product_sets: [...productSetsById.values()],\n counts: {\n catalogs: catalogsById.size,\n products: productsById.size,\n product_sets: productSetsById.size,\n },\n join_keys: [\n \"product.id\",\n \"product.retailer_id\",\n \"retailer_product_group_id\",\n \"brand\",\n \"category\",\n \"product_type\",\n \"custom_label_0..4\",\n ],\n paging,\n warnings,\n });\n },\n );\n\n // ── P2. meta_join_product_insights ────────────────────────────────\n server.tool(\n \"meta_join_product_insights\",\n \"Query product-breakdown insights and enrich rows with Product Catalog metadata when catalog access is available.\",\n {\n adAccountId: adAccountIdSchema,\n catalogId: z.string().optional().describe(\"Product Catalog ID used to load product metadata for joins.\"),\n productBreakdown: productBreakdownSchema.optional().default(\"product_id\"),\n metrics: z.array(z.string()).min(1).optional().default([\"impressions\", \"clicks\", \"spend\", \"actions\", \"action_values\"]),\n level: levelSchema.optional().default(\"ad\"),\n datePreset: datePresetSchema.describe(\"Insights date preset. Defaults to last_30d.\"),\n timeRange: timeRangeSchema.describe(\"Optional custom insights date range.\"),\n limit: z.number().int().min(1).max(1000).optional().default(500),\n catalogProductLimit: z.number().int().min(1).max(1000).optional().default(500),\n },\n async ({ adAccountId, catalogId, productBreakdown, metrics, level, datePreset, timeRange, limit, catalogProductLimit }) => {\n const warnings: MetaWarning[] = [];\n const insightFields = Array.from(new Set([\n \"campaign_id\",\n \"campaign_name\",\n \"adset_id\",\n \"adset_name\",\n \"ad_id\",\n \"ad_name\",\n ...metrics,\n ]));\n let insightRows: Record<string, unknown>[] = [];\n const products: Record<string, unknown>[] = [];\n const productFields = [\n \"id\",\n \"retailer_id\",\n \"name\",\n \"brand\",\n \"category\",\n \"fb_product_category\",\n \"product_type\",\n \"price\",\n \"sale_price\",\n \"currency\",\n \"availability\",\n \"inventory\",\n \"url\",\n \"image_url\",\n \"custom_label_0\",\n \"custom_label_1\",\n \"custom_label_2\",\n \"custom_label_3\",\n \"custom_label_4\",\n \"retailer_product_group_id\",\n \"product_catalog{id,name}\",\n \"product_group{id}\",\n ].join(\",\");\n const productFallbackFields = \"id,retailer_id,name,brand,price,availability,url,image_url\";\n\n try {\n insightRows = (await client.fetchInsights(formatAdAccountId(adAccountId), {\n type: \"product_insights_join\",\n fields: insightFields,\n level,\n limit,\n datePreset: timeRange ? undefined : (datePreset ?? \"last_30d\"),\n timeRange,\n breakdowns: [productBreakdown],\n })).filter(isRecord);\n } catch (error) {\n warnings.push(warningFromError(\n \"product_insights\",\n error,\n \"Product breakdowns are not available for every objective, catalog setup, or metric set. Try product_id with basic delivery metrics first.\",\n ));\n }\n\n if (catalogId) {\n const catalogProducts = await fetchGraphWithFallback(\n client,\n \"catalog_products_for_join\",\n graphUrl(client, `/${catalogId}/products`, { fields: productFields, limit: catalogProductLimit }),\n graphUrl(client, `/${catalogId}/products`, { fields: productFallbackFields, limit: catalogProductLimit }),\n warnings,\n \"Catalog product enrichment requires catalog asset access. Insight rows are still returned when catalog reads fail.\",\n );\n products.push(\n ...dataArray(catalogProducts).map((product) => normalizeProductItem(product, `catalog:${catalogId}:products`)),\n );\n } else {\n warnings.push({\n area: \"catalog_join\",\n message: \"No catalogId was provided, so insight rows cannot be enriched with Product Catalog metadata.\",\n suggestion: \"Run meta_get_catalog_products with businessId to discover a catalogId, then rerun this tool.\",\n });\n }\n\n const lookup = buildProductLookup(products, productBreakdown);\n let matchedRows = 0;\n const enrichedRows = insightRows.map((row) => {\n const key = typeof row[productBreakdown] === \"string\" ? row[productBreakdown] : undefined;\n const matches = key ? lookup.get(key) ?? [] : [];\n if (matches.length > 0) matchedRows += 1;\n return {\n ...row,\n catalog_join: {\n breakdown: productBreakdown,\n key,\n matched: matches.length > 0,\n match_count: matches.length,\n product_group: key && matches.length > 0 ? summarizeProductGroup(key, matches) : undefined,\n },\n };\n });\n\n return ok({\n ad_account_id: formatAdAccountId(adAccountId),\n date_range: timeRange ?? { date_preset: datePreset ?? \"last_30d\" },\n product_breakdown: productBreakdown,\n rows: enrichedRows,\n counts: {\n insight_rows: insightRows.length,\n matched_rows: matchedRows,\n unmatched_rows: insightRows.length - matchedRows,\n catalog_products_loaded: products.length,\n },\n warnings,\n });\n },\n );\n\n // ── P2. meta_get_brand_safety_controls ────────────────────────────\n server.tool(\n \"meta_get_brand_safety_controls\",\n \"Read brand safety, suitability, placement, and context-control signals from ad account/ad set targeting and optional block-list edges.\",\n {\n adAccountId: adAccountIdSchema,\n businessId: z.string().optional().describe(\"Optional Business Manager ID for block-list discovery.\"),\n adsetIds: z.array(z.string()).max(100).optional().describe(\"Specific ad set IDs to inspect. If omitted, reads ad sets from the ad account.\"),\n includeAdsets: z.boolean().optional().default(true),\n includeBlockLists: z.boolean().optional().default(true),\n includeRawTargeting: z.boolean().optional().default(false),\n limit: z.number().int().min(1).max(500).optional().default(100),\n cursor: cursorSchema,\n },\n async ({ adAccountId, businessId, adsetIds, includeAdsets, includeBlockLists, includeRawTargeting, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const blockListsById = new Map<string, Record<string, unknown>>();\n const paging: Record<string, unknown> = {};\n const account = await fetchGraph(\n client,\n \"brand_safety_ad_account\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}`, { fields: \"id,name,account_status,business{id,name}\" }),\n warnings,\n \"Grant ads_read and confirm the token can read this ad account.\",\n );\n const adsetFields = \"id,name,status,effective_status,campaign_id,targeting,optimization_goal,billing_event,promoted_object\";\n const adsetFallbackFields = \"id,name,status,effective_status,campaign_id,targeting\";\n const blockListFields = \"id,name,owner_business,created_time,updated_time\";\n\n let adsets: Record<string, unknown>[] = [];\n if (includeAdsets) {\n if (adsetIds && adsetIds.length > 0) {\n const result = await fetchGraphWithFallback(\n client,\n \"brand_safety_adset_ids\",\n graphUrl(client, \"\", { ids: adsetIds.join(\",\"), fields: adsetFields }),\n graphUrl(client, \"\", { ids: adsetIds.join(\",\"), fields: adsetFallbackFields }),\n warnings,\n \"Some ad set targeting fields are permission/version dependent; minimal targeting is returned when rich fields fail.\",\n );\n adsets = Object.values(result ?? {}).filter(isRecord);\n } else {\n const result = await fetchGraphWithFallback(\n client,\n \"brand_safety_adsets\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/adsets`, {\n fields: adsetFields,\n limit,\n after: cursor,\n }),\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/adsets`, {\n fields: adsetFallbackFields,\n limit,\n after: cursor,\n }),\n warnings,\n \"Some ad set targeting fields are permission/version dependent; minimal targeting is returned when rich fields fail.\",\n );\n adsets = dataArray(result);\n paging.adsets = pagingInfo(result);\n }\n }\n\n if (includeBlockLists) {\n const adAccountBlockLists = await fetchGraph(\n client,\n \"ad_account_publisher_block_lists\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/publisher_block_lists`, {\n fields: blockListFields,\n limit,\n after: cursor,\n }),\n warnings,\n \"Publisher/content block-list Graph edges are not available for every account, API version, or rollout. Use Business Manager Brand Safety and Suitability Center when this edge is unavailable.\",\n );\n addById(blockListsById, dataArray(adAccountBlockLists));\n paging.ad_account_publisher_block_lists = pagingInfo(adAccountBlockLists);\n\n if (businessId) {\n const [ownedBlockLists, clientBlockLists] = await Promise.all([\n fetchGraph(\n client,\n \"business_owned_publisher_block_lists\",\n graphUrl(client, `/${businessId}/owned_publisher_block_lists`, {\n fields: blockListFields,\n limit,\n after: cursor,\n }),\n warnings,\n \"Owned publisher block-list edges are rollout/permission dependent; verify business_management and Brand Safety Center access.\",\n ),\n fetchGraph(\n client,\n \"business_client_publisher_block_lists\",\n graphUrl(client, `/${businessId}/client_publisher_block_lists`, {\n fields: blockListFields,\n limit,\n after: cursor,\n }),\n warnings,\n \"Client publisher block-list edges may require partner permissions and may not be exposed for this business.\",\n ),\n ]);\n addById(blockListsById, dataArray(ownedBlockLists));\n addById(blockListsById, dataArray(clientBlockLists));\n paging.business_owned_publisher_block_lists = pagingInfo(ownedBlockLists);\n paging.business_client_publisher_block_lists = pagingInfo(clientBlockLists);\n }\n }\n\n return ok({\n ad_account_id: formatAdAccountId(adAccountId),\n account,\n adset_context_controls: adsets.map((adset) => ({\n ...summarizeBrandSafetyTargeting(adset),\n raw_targeting: includeRawTargeting ? adset.targeting : undefined,\n })),\n publisher_block_lists: [...blockListsById.values()],\n counts: {\n adsets: adsets.length,\n publisher_block_lists: blockListsById.size,\n },\n notes: [\n \"Brand safety Graph coverage varies by account, region, and rollout.\",\n \"This tool only reads ad set targeting and block-list metadata; it does not create, upload, apply, or remove controls.\",\n ],\n paging,\n warnings,\n });\n },\n );\n\n // ── P2. meta_interpret_experiment_results ─────────────────────────\n server.tool(\n \"meta_interpret_experiment_results\",\n \"Read and interpret A/B test or conversion lift study results with confidence guardrails, cells, objectives, and optional cell entities.\",\n {\n studyId: z.string().optional().describe(\"Specific Ad Study ID to interpret.\"),\n adAccountId: adAccountIdSchema.optional().describe(\"Ad account ID used to discover ad_studies when studyId is omitted.\"),\n includeCellEntities: z.boolean().optional().default(false).describe(\"Also read campaigns/adsets/adaccounts attached to each study cell.\"),\n cellEntityType: z.enum([\"campaigns\", \"adsets\", \"adaccounts\"]).optional().default(\"campaigns\"),\n limit: z.number().int().min(1).max(100).optional().default(50),\n },\n async ({ studyId, adAccountId, includeCellEntities, cellEntityType, limit }) => {\n const warnings: MetaWarning[] = [];\n const studiesById = new Map<string, Record<string, unknown>>();\n const studyFields = [\n \"id\",\n \"name\",\n \"type\",\n \"description\",\n \"created_time\",\n \"start_time\",\n \"end_time\",\n \"results_first_available_date\",\n \"canceled_time\",\n \"observation_end_time\",\n \"cooldown_start_time\",\n ].join(\",\");\n const objectiveFields = \"id,name,type,is_primary,last_updated_results,results,custom_attributes\";\n const objectiveFallbackFields = \"id,name,type,is_primary,last_updated_results\";\n const cellFields = \"id,name,treatment_percentage,control_percentage,ad_entities_count\";\n\n if (studyId) {\n const study = await fetchGraph(\n client,\n \"ad_study\",\n graphUrl(client, `/${studyId}`, { fields: studyFields }),\n warnings,\n \"Verify the Ad Study ID is readable by this token.\",\n );\n studiesById.set(studyId, study ?? { id: studyId });\n }\n\n if (!studyId && adAccountId) {\n const accountStudies = await fetchGraph(\n client,\n \"ad_account_ad_studies\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/ad_studies`, {\n fields: studyFields,\n limit,\n }),\n warnings,\n \"Ad Study account edges are permission dependent. Verify ads_read and experiment access for this account.\",\n );\n addById(studiesById, dataArray(accountStudies));\n\n const accountBusiness = await fetchGraph(\n client,\n \"ad_study_account_business\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}`, { fields: \"business{id,name}\" }),\n warnings,\n \"Business-level study fallback requires business access on the ad account.\",\n );\n const business = isRecord(accountBusiness?.business) ? accountBusiness.business : undefined;\n if (typeof business?.id === \"string\") {\n const businessStudies = await fetchGraph(\n client,\n \"business_ad_studies\",\n graphUrl(client, `/${business.id}/ad_studies`, {\n fields: studyFields,\n limit,\n }),\n warnings,\n \"Business ad_studies may require business_management or experiment access.\",\n );\n addById(studiesById, dataArray(businessStudies));\n }\n\n const meStudies = await fetchGraph(\n client,\n \"me_ad_studies\",\n graphUrl(client, \"/me/ad_studies\", {\n fields: studyFields,\n limit,\n }),\n warnings,\n \"The /me/ad_studies fallback is not available for every token or app mode.\",\n );\n addById(studiesById, dataArray(meStudies));\n }\n\n if (!studyId && !adAccountId) {\n warnings.push({\n area: \"ad_studies\",\n message: \"No studyId or adAccountId was provided.\",\n suggestion: \"Pass studyId for targeted interpretation or adAccountId to discover readable studies.\",\n });\n }\n\n const interpretedStudies: Record<string, unknown>[] = [];\n for (const study of studiesById.values()) {\n const id = typeof study.id === \"string\" ? study.id : undefined;\n if (!id) continue;\n\n const [cellsResult, objectivesResult] = await Promise.all([\n fetchGraph(\n client,\n `ad_study:${id}:cells`,\n graphUrl(client, `/${id}/cells`, { fields: cellFields, limit }),\n warnings,\n \"Study cells may require experiment access. Without cells, interpretation is limited to objective payloads.\",\n ),\n fetchGraphWithFallback(\n client,\n `ad_study:${id}:objectives`,\n graphUrl(client, `/${id}/objectives`, { fields: objectiveFields, limit }),\n graphUrl(client, `/${id}/objectives`, { fields: objectiveFallbackFields, limit }),\n warnings,\n \"Study result payloads can be permission/whitelist dependent; basic objective fields are returned when full results are unavailable.\",\n ),\n ]);\n\n const cells = dataArray(cellsResult);\n const objectives = dataArray(objectivesResult);\n const cellEntities: Record<string, unknown> = {};\n if (includeCellEntities) {\n for (const cell of cells) {\n const cellId = typeof cell.id === \"string\" ? cell.id : undefined;\n if (!cellId) continue;\n const entities = await fetchGraph(\n client,\n `ad_study_cell:${cellId}:${cellEntityType}`,\n graphUrl(client, `/${cellId}/${cellEntityType}`, {\n fields: \"id,name\",\n limit,\n }),\n warnings,\n \"Cell entity edges vary by study type. Try a different cellEntityType if this edge is unavailable.\",\n );\n cellEntities[cellId] = dataArray(entities);\n }\n }\n\n const interpretedObjectives = objectives.map(interpretStudyObjective);\n interpretedStudies.push({\n ...study,\n inferred_status: inferStudyStatus(study),\n cells,\n objectives: interpretedObjectives,\n cell_entities: includeCellEntities ? cellEntities : undefined,\n interpretation_notes: [\n \"Confidence below 90% should not be treated as a winner.\",\n \"Lift studies estimate incrementality against a holdout; A/B tests compare variants.\",\n \"If result fields are absent, Meta did not expose the full results payload for this token or study type.\",\n ],\n });\n }\n\n return ok({\n studies: interpretedStudies,\n count: interpretedStudies.length,\n warnings,\n });\n },\n );\n\n // ── P2. meta_get_organic_content_enrichment ───────────────────────\n server.tool(\n \"meta_get_organic_content_enrichment\",\n \"Enrich Facebook Page posts and Instagram organic media with media URLs, permalinks, counts, attachments, and optional read-only insights.\",\n {\n pageId: z.string().optional().describe(\"Facebook Page ID for /posts and linked IG discovery.\"),\n instagramAccountId: z.string().optional().describe(\"Instagram professional account ID for /media.\"),\n postIds: z.array(z.string()).max(50).optional().describe(\"Specific Facebook post IDs to enrich.\"),\n mediaIds: z.array(z.string()).max(50).optional().describe(\"Specific Instagram media IDs to enrich.\"),\n includePagePosts: z.boolean().optional().default(true),\n includeInstagramMedia: z.boolean().optional().default(true),\n includeInsights: z.boolean().optional().default(false),\n limit: z.number().int().min(1).max(100).optional().default(25),\n cursor: cursorSchema,\n since: z.string().optional().describe(\"Optional Graph time filter. For IG media this should be Unix seconds or strtotime-compatible text.\"),\n until: z.string().optional().describe(\"Optional Graph time filter. For IG media this should be Unix seconds or strtotime-compatible text.\"),\n },\n async ({ pageId, instagramAccountId, postIds, mediaIds, includePagePosts, includeInstagramMedia, includeInsights, limit, cursor, since, until }) => {\n const warnings: MetaWarning[] = [];\n const paging: Record<string, unknown> = {};\n const pagePostFields = includeInsights\n ? \"id,message,created_time,type,permalink_url,full_picture,attachments{media,type,url,target,title,description},shares,likes.summary(true),comments.summary(true),insights.metric(post_impressions,post_engaged_users,post_clicks)\"\n : \"id,message,created_time,type,permalink_url,full_picture,attachments{media,type,url,target,title,description},shares,likes.summary(true),comments.summary(true)\";\n const pagePostFallbackFields = \"id,message,created_time,type,permalink_url,full_picture,shares,likes.summary(true),comments.summary(true)\";\n const igMediaFields = \"id,caption,media_type,media_url,thumbnail_url,permalink,timestamp,username,like_count,comments_count,children{id,media_type,media_url,thumbnail_url,permalink}\";\n const pagePosts: Record<string, unknown>[] = [];\n const instagramMedia: Record<string, unknown>[] = [];\n let resolvedInstagramAccountId = instagramAccountId;\n\n if (!pageId && !instagramAccountId && (!postIds || postIds.length === 0) && (!mediaIds || mediaIds.length === 0)) {\n warnings.push({\n area: \"organic_content\",\n message: \"No pageId, instagramAccountId, postIds, or mediaIds were provided.\",\n suggestion: \"Pass pageId for Page posts and linked IG discovery, or instagramAccountId/mediaIds for IG media enrichment.\",\n });\n }\n\n if (includePagePosts && postIds && postIds.length > 0) {\n const result = await fetchGraphWithFallback(\n client,\n \"page_post_ids\",\n graphUrl(client, \"\", { ids: postIds.join(\",\"), fields: pagePostFields }),\n graphUrl(client, \"\", { ids: postIds.join(\",\"), fields: pagePostFallbackFields }),\n warnings,\n \"Post insights or attachments may require pages_read_engagement or a Page token; minimal post metadata is returned when rich fields fail.\",\n );\n pagePosts.push(...Object.values(result ?? {}).filter(isRecord).map(normalizeOrganicPost));\n } else if (includePagePosts && pageId) {\n const result = await fetchGraphWithFallback(\n client,\n \"page_posts_enrichment\",\n graphUrl(client, `/${pageId}/posts`, {\n fields: pagePostFields,\n limit,\n after: cursor,\n since,\n until,\n }),\n graphUrl(client, `/${pageId}/posts`, {\n fields: pagePostFallbackFields,\n limit,\n after: cursor,\n since,\n until,\n }),\n warnings,\n \"Post insights or attachment fields may require pages_read_engagement and Page access. Minimal post fields are returned when rich fields fail.\",\n );\n pagePosts.push(...dataArray(result).map(normalizeOrganicPost));\n paging.page_posts = pagingInfo(result);\n }\n\n if (includeInstagramMedia && !resolvedInstagramAccountId && pageId) {\n const page = await fetchGraph(\n client,\n \"page_linked_instagram_for_media\",\n graphUrl(client, `/${pageId}`, {\n fields: \"id,name,instagram_business_account{id,username,name},connected_instagram_account{id,username,name}\",\n }),\n warnings,\n \"Grant pages_show_list/pages_read_engagement plus instagram_basic to discover linked Instagram accounts from the Page.\",\n );\n const businessAccount = isRecord(page?.instagram_business_account) ? page.instagram_business_account : undefined;\n const connectedAccount = isRecord(page?.connected_instagram_account) ? page.connected_instagram_account : undefined;\n resolvedInstagramAccountId = typeof businessAccount?.id === \"string\"\n ? businessAccount.id\n : typeof connectedAccount?.id === \"string\"\n ? connectedAccount.id\n : undefined;\n }\n\n if (includeInstagramMedia && mediaIds && mediaIds.length > 0) {\n const result = await fetchGraph(\n client,\n \"instagram_media_ids\",\n graphUrl(client, \"\", { ids: mediaIds.join(\",\"), fields: igMediaFields }),\n warnings,\n \"Verify the IG media IDs are readable and the token has instagram_basic plus Page access.\",\n );\n instagramMedia.push(...Object.values(result ?? {}).filter(isRecord).map(normalizeInstagramMedia));\n } else if (includeInstagramMedia && resolvedInstagramAccountId) {\n const result = await fetchGraph(\n client,\n \"instagram_media\",\n graphUrl(client, `/${resolvedInstagramAccountId}/media`, {\n fields: igMediaFields,\n limit,\n after: cursor,\n since,\n until,\n }),\n warnings,\n \"IG media reads require instagram_basic and Page access. Story media uses a separate stories endpoint and is not fetched here.\",\n );\n instagramMedia.push(...dataArray(result).map(normalizeInstagramMedia));\n paging.instagram_media = pagingInfo(result);\n }\n\n if (includeInsights && instagramMedia.length > 0) {\n for (const media of instagramMedia) {\n const mediaId = typeof media.id === \"string\" ? media.id : undefined;\n if (!mediaId) continue;\n const insights = await fetchGraph(\n client,\n `instagram_media:${mediaId}:insights`,\n graphUrl(client, `/${mediaId}/insights`, {\n metric: \"impressions,reach,engagement,saved,video_views,plays,total_interactions\",\n }),\n warnings,\n \"IG media insight metrics vary by media type and API version. Basic media metadata remains available when insights fail.\",\n );\n media.insights = insights;\n }\n }\n\n return ok({\n page_id: pageId,\n instagram_account_id: resolvedInstagramAccountId,\n page_posts: pagePosts,\n instagram_media: instagramMedia,\n counts: {\n page_posts: pagePosts.length,\n instagram_media: instagramMedia.length,\n },\n notes: [\n \"Instagram thumbnail_url is media-type dependent and may only appear for video/Reels media.\",\n \"This tool only reads existing organic content; it never publishes, edits, hides, or deletes posts.\",\n ],\n paging,\n warnings,\n });\n },\n );\n\n // ── 1. meta_list_ad_accounts ──────────────────────────────────────\n server.tool(\n \"meta_list_ad_accounts\",\n \"List all Meta ad accounts accessible with the current token. Returns account ID, name, currency, timezone, and status.\",\n {},\n async () => {\n try {\n const accounts = await client.getAdAccounts();\n return ok({ accounts, count: accounts.length });\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 2. meta_get_account_details ───────────────────────────────────\n server.tool(\n \"meta_get_account_details\",\n \"Get detailed information for a specific Meta ad account: name, currency, timezone, spend cap, status, business info.\",\n { adAccountId: adAccountIdSchema },\n async ({ adAccountId }) => {\n try {\n const account = await client.getAdAccount(adAccountId);\n return ok(account);\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 3. meta_get_campaigns ─────────────────────────────────────────\n server.tool(\n \"meta_get_campaigns\",\n \"List campaigns for a Meta ad account. Returns campaign ID, name, status, objective, budget, and bid strategy.\",\n {\n adAccountId: adAccountIdSchema,\n statusFilter: statusFilterSchema,\n limit: limitSchema,\n cursor: cursorSchema,\n },\n async ({ adAccountId, statusFilter, limit, cursor }) => {\n try {\n const fields = \"id,name,status,effective_status,objective,daily_budget,lifetime_budget,budget_remaining,bid_strategy,buying_type,start_time,stop_time\";\n let url = `https://graph.facebook.com/${client.apiVersion}/${adAccountId}/campaigns?fields=${fields}&limit=${limit}`;\n if (statusFilter) url += `&filtering=[{\"field\":\"effective_status\",\"operator\":\"IN\",\"value\":[\"${statusFilter}\"]}]`;\n if (cursor) url += `&after=${cursor}`;\n const result = await client.fetchUrl(url);\n return ok(result);\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 4. meta_get_adsets ────────────────────────────────────────────\n server.tool(\n \"meta_get_adsets\",\n \"List ad sets for a Meta ad account, optionally filtered by campaign. Returns targeting, budget, optimization, and schedule info.\",\n {\n adAccountId: adAccountIdSchema,\n campaignId: z.string().optional().describe(\"Filter by campaign ID\"),\n statusFilter: statusFilterSchema,\n limit: limitSchema,\n cursor: cursorSchema,\n },\n async ({ adAccountId, campaignId, statusFilter, limit, cursor }) => {\n try {\n const fields = \"id,name,status,effective_status,campaign_id,daily_budget,lifetime_budget,optimization_goal,billing_event,bid_amount,targeting,start_time,end_time\";\n const parent = campaignId ?? adAccountId;\n let url = `https://graph.facebook.com/${client.apiVersion}/${parent}/adsets?fields=${fields}&limit=${limit}`;\n if (statusFilter) url += `&filtering=[{\"field\":\"effective_status\",\"operator\":\"IN\",\"value\":[\"${statusFilter}\"]}]`;\n if (cursor) url += `&after=${cursor}`;\n const result = await client.fetchUrl(url);\n return ok(result);\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 5. meta_get_ads ───────────────────────────────────────────────\n server.tool(\n \"meta_get_ads\",\n \"List ads for a Meta ad account, optionally filtered by ad set. Returns ad ID, name, status, and creative reference.\",\n {\n adAccountId: adAccountIdSchema,\n adsetId: z.string().optional().describe(\"Filter by ad set ID\"),\n statusFilter: statusFilterSchema,\n limit: limitSchema,\n cursor: cursorSchema,\n },\n async ({ adAccountId, adsetId, statusFilter, limit, cursor }) => {\n try {\n const fields = \"id,name,status,effective_status,adset_id,campaign_id,creative{id,name,thumbnail_url,object_story_spec,asset_feed_spec}\";\n const parent = adsetId ?? adAccountId;\n let url = `https://graph.facebook.com/${client.apiVersion}/${parent}/ads?fields=${fields}&limit=${limit}`;\n if (statusFilter) url += `&filtering=[{\"field\":\"effective_status\",\"operator\":\"IN\",\"value\":[\"${statusFilter}\"]}]`;\n if (cursor) url += `&after=${cursor}`;\n const result = await client.fetchUrl(url);\n return ok(result);\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 6. meta_get_insights ──────────────────────────────────────────\n server.tool(\n \"meta_get_insights\",\n `Query Meta Ads performance insights. Supports 170+ metrics, 43+ breakdowns with intelligent query planning.\nUse meta://metrics resource to see available metrics. Use meta://breakdowns for available breakdowns.\nThe query planner automatically splits incompatible metric/breakdown combinations into multiple API requests and merges results.`,\n {\n adAccountId: adAccountIdSchema,\n level: levelSchema.describe(\"Aggregation level: account, campaign, adset, or ad\"),\n metrics: z.array(z.string()).min(1).describe(\"Metric keys from meta://metrics (e.g., impressions, spend, ctr)\"),\n breakdowns: z.array(z.string()).optional().describe(\"Breakdown keys from meta://breakdowns (e.g., age, gender, country)\"),\n datePreset: datePresetSchema.describe(\"Predefined date range (e.g., last_7d, last_30d)\"),\n timeRange: timeRangeSchema.describe(\"Custom date range with since/until in YYYY-MM-DD format\"),\n timeIncrement: z.union([z.literal(1), z.literal(7), z.literal(14), z.literal(28), z.literal(30), z.string()]).optional().describe(\"Time granularity: 1 (daily), 7 (weekly), 'monthly', or 'all_days'\"),\n limit: z.number().int().min(1).max(5000).optional().default(500),\n },\n async ({ adAccountId, level, metrics, breakdowns, datePreset, timeRange, timeIncrement, limit }) => {\n try {\n const startTime = Date.now();\n\n // Plan the query\n const plan = planQueries({\n adAccountId,\n level,\n metrics,\n breakdowns: breakdowns ?? [],\n datePreset: (datePreset ?? \"last_30d\") as import(\"./types.js\").DatePreset,\n timeRange,\n timeIncrement: timeIncrement as import(\"./types.js\").TimeIncrement | undefined,\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 suggestion: \"Check meta://compatibility for valid metric/breakdown combinations\",\n });\n }\n\n // Execute all requests\n const allResults: Record<string, unknown>[][] = [];\n for (const request of plan.requests) {\n const result = await client.fetchInsights(adAccountId, request);\n allResults.push(result);\n }\n\n // Merge results\n let data = mergeResults(allResults, plan.joinKeys);\n\n // Calculate derived metrics\n if (plan.calculatedMetrics.length > 0) {\n data = data.map((row) => ({\n ...row,\n ...calculateDerivedMetrics(row as import(\"./types.js\").InsightRow),\n }));\n }\n\n return ok({\n data,\n rowCount: data.length,\n debug: {\n requestCount: plan.requests.length,\n executionTimeMs: Date.now() - startTime,\n warnings: plan.warnings,\n calculatedMetrics: plan.calculatedMetrics,\n },\n });\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 7. meta_get_campaign_structure ────────────────────────────────\n server.tool(\n \"meta_get_campaign_structure\",\n \"Get hierarchical campaign structure: campaigns -> ad sets -> ads. Useful for understanding account organization.\",\n {\n adAccountId: adAccountIdSchema,\n campaignId: z.string().optional().describe(\"Get structure for a specific campaign only\"),\n },\n async ({ adAccountId, campaignId }) => {\n try {\n const campaignFields = \"id,name,status,effective_status,objective\";\n const adsetFields = \"id,name,status,effective_status,campaign_id,optimization_goal\";\n const adFields = \"id,name,status,effective_status,adset_id,campaign_id\";\n\n const parent = campaignId ?? adAccountId;\n\n if (campaignId) {\n const [adsets, ads] = await Promise.all([\n client.fetchUrl(`https://graph.facebook.com/${client.apiVersion}/${campaignId}/adsets?fields=${adsetFields}&limit=100`),\n client.fetchUrl(`https://graph.facebook.com/${client.apiVersion}/${campaignId}/ads?fields=${adFields}&limit=100`),\n ]);\n const campaign = await client.fetchUrl(`https://graph.facebook.com/${client.apiVersion}/${campaignId}?fields=${campaignFields}`);\n return ok({ campaign, adsets: adsets.data, ads: ads.data });\n }\n\n const campaigns = await client.fetchUrl(\n `https://graph.facebook.com/${client.apiVersion}/${parent}/campaigns?fields=${campaignFields}&limit=50`,\n );\n const campaignList = campaigns.data as unknown[] ?? [];\n return ok({ campaigns: campaignList, totalCampaigns: campaignList.length });\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 8. meta_get_creatives ─────────────────────────────────────────\n server.tool(\n \"meta_get_creatives\",\n \"Get ad creative content: text, images, videos, links, call-to-action. Returns creative details for specified ads.\",\n {\n adAccountId: adAccountIdSchema,\n adIds: z.array(z.string()).optional().describe(\"Specific ad IDs to get creatives for\"),\n limit: limitSchema,\n },\n async ({ adAccountId, adIds, limit }) => {\n try {\n const fields = \"id,name,status,creative{id,name,body,title,image_url,image_hash,video_id,thumbnail_url,link_url,object_story_spec,asset_feed_spec,call_to_action_type}\";\n let url: string;\n if (adIds && adIds.length > 0) {\n const ids = adIds.join(\",\");\n url = `https://graph.facebook.com/${client.apiVersion}/?ids=${ids}&fields=${fields}`;\n } else {\n url = `https://graph.facebook.com/${client.apiVersion}/${adAccountId}/ads?fields=${fields}&limit=${limit}`;\n }\n const result = await client.fetchUrl(url);\n return ok(result);\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 9. meta_get_audiences ─────────────────────────────────────────\n server.tool(\n \"meta_get_audiences\",\n \"List custom, saved, and lookalike audiences for a Meta ad account.\",\n {\n adAccountId: adAccountIdSchema,\n type: z.enum([\"custom\", \"saved\", \"lookalike\"]).optional().describe(\"Filter by audience type\"),\n limit: limitSchema,\n },\n async ({ adAccountId, type, limit }) => {\n try {\n const endpoint = type === \"saved\" ? \"saved_audiences\" : \"customaudiences\";\n const fields = type === \"saved\"\n ? \"id,name,targeting,description\"\n : \"id,name,subtype,approximate_count_lower_bound,approximate_count_upper_bound,delivery_status,description,time_created\";\n let url = `https://graph.facebook.com/${client.apiVersion}/${adAccountId}/${endpoint}?fields=${fields}&limit=${limit}`;\n if (type === \"lookalike\") {\n url += `&filtering=[{\"field\":\"subtype\",\"operator\":\"EQUAL\",\"value\":\"LOOKALIKE\"}]`;\n }\n const result = await client.fetchUrl(url);\n return ok(result);\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 10. meta_get_ad_studies ────────────────────────────────────────\n server.tool(\n \"meta_get_ad_studies\",\n \"List conversion lift studies and A/B tests (Ad Studies) for an ad account. Includes study type, status, and cells.\",\n { adAccountId: adAccountIdSchema },\n async ({ adAccountId }) => {\n try {\n const studies = await client.getAdStudies(adAccountId);\n return ok({ studies, count: studies.length });\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 11. meta_get_study_results ────────────────────────────────────\n server.tool(\n \"meta_get_study_results\",\n \"Get detailed results for a conversion lift or A/B test study. Returns objectives, cells, and lift results.\",\n { studyId: z.string().describe(\"The Ad Study ID\") },\n async ({ studyId }) => {\n try {\n const [cells, objectives] = await Promise.all([\n client.getAdStudyCells(studyId),\n client.getAdStudyObjectives(studyId).catch(() => client.getAdStudyObjectivesBasic(studyId)),\n ]);\n return ok({ studyId, cells, objectives });\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 12. meta_validate_query ───────────────────────────────────────\n server.tool(\n \"meta_validate_query\",\n \"Validate a metric/breakdown combination BEFORE executing. Returns errors and warnings. Use this to check if your query will work.\",\n {\n metrics: z.array(z.string()).min(1).describe(\"Metric keys to validate\"),\n breakdowns: z.array(z.string()).optional().describe(\"Breakdown keys to validate\"),\n level: levelSchema.optional().describe(\"Aggregation level\"),\n },\n async ({ metrics, breakdowns, level }) => {\n try {\n const result = validateQuerySelection(metrics, breakdowns ?? [], [], level ?? \"campaign\");\n return ok(result);\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 13. meta_get_page_posts ───────────────────────────────────────\n server.tool(\n \"meta_get_page_posts\",\n \"Get recent posts from a connected Facebook/Instagram page. Requires page access.\",\n {\n pageId: z.string().describe(\"Facebook Page ID\"),\n limit: z.number().int().min(1).max(100).optional().default(25),\n },\n async ({ pageId, limit }) => {\n try {\n const fields = \"id,message,created_time,type,permalink_url,full_picture,shares,likes.summary(true),comments.summary(true)\";\n const url = `https://graph.facebook.com/${client.apiVersion}/${pageId}/posts?fields=${fields}&limit=${limit}`;\n const result = await client.fetchUrl(url);\n return ok(result);\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 14. meta_debug_token ──────────────────────────────────────────\n server.tool(\n \"meta_debug_token\",\n \"Check the current access token validity, expiration, and granted scopes. Useful for diagnosing auth issues.\",\n {},\n async () => {\n try {\n const info = await client.debugToken();\n return ok(info);\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n // ── 15. meta_search_entities ──────────────────────────────────────\n server.tool(\n \"meta_search_entities\",\n \"Search campaigns, ad sets, or ads by name within an ad account. Useful for finding specific entities.\",\n {\n adAccountId: adAccountIdSchema,\n entityType: z.enum([\"campaigns\", \"adsets\", \"ads\"]).describe(\"Type of entity to search\"),\n nameFilter: z.string().describe(\"Name substring to search for\"),\n statusFilter: statusFilterSchema,\n limit: limitSchema,\n },\n async ({ adAccountId, entityType, nameFilter, statusFilter, limit }) => {\n try {\n const fieldsMap = {\n campaigns: \"id,name,status,effective_status,objective\",\n adsets: \"id,name,status,effective_status,campaign_id,optimization_goal\",\n ads: \"id,name,status,effective_status,adset_id,campaign_id\",\n };\n const fields = fieldsMap[entityType];\n const filters = [{ field: \"name\", operator: \"CONTAIN\", value: nameFilter }];\n if (statusFilter) {\n filters.push({ field: \"effective_status\", operator: \"IN\", value: statusFilter } as { field: string; operator: string; value: string });\n }\n const url = `https://graph.facebook.com/${client.apiVersion}/${adAccountId}/${entityType}?fields=${fields}&limit=${limit}&filtering=${encodeURIComponent(JSON.stringify(filters))}`;\n const result = await client.fetchUrl(url);\n return ok(result);\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n registerMetaBroadReadTools(server, client, ok);\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { Platform } from \"./types.js\";\n\n/**\n * Base error for all platform API errors.\n * Provides structured error information for MCP tool responses.\n */\nexport class PlatformApiError extends Error {\n constructor(\n public readonly platform: Platform,\n public readonly code: number,\n message: string,\n public readonly isRateLimit: boolean = false,\n public readonly isAuth: boolean = false,\n public readonly isPermission: boolean = false,\n public readonly suggestion: string = \"\",\n public readonly retryAfter?: number,\n ) {\n super(message);\n this.name = \"PlatformApiError\";\n }\n\n toMcpError() {\n return {\n error: this.message,\n platform: this.platform,\n code: this.code,\n isRateLimit: this.isRateLimit,\n isAuth: this.isAuth,\n isPermission: this.isPermission,\n suggestion: this.suggestion,\n ...(this.retryAfter !== undefined && { retryAfter: this.retryAfter }),\n };\n }\n}\n\nexport class RateLimitError extends PlatformApiError {\n constructor(platform: Platform, retryAfter?: number) {\n super(\n platform,\n 429,\n `Rate limit exceeded for ${platform}`,\n true,\n false,\n false,\n retryAfter\n ? `Wait ${retryAfter} seconds and retry`\n : \"Wait and retry with exponential backoff\",\n retryAfter,\n );\n this.name = \"RateLimitError\";\n }\n}\n\nexport class AuthError extends PlatformApiError {\n constructor(platform: Platform, message?: string) {\n super(\n platform,\n 401,\n message ?? `Authentication failed for ${platform}. Check your access token.`,\n false,\n true,\n false,\n `Verify your ${platform.toUpperCase()} access token is valid and has the required scopes`,\n );\n this.name = \"AuthError\";\n }\n}\n\nexport class PermissionError extends PlatformApiError {\n constructor(platform: Platform, message?: string) {\n super(\n platform,\n 403,\n message ?? `Insufficient permissions for ${platform}`,\n false,\n false,\n true,\n `Ensure your token has the required read-only scopes for ${platform}`,\n );\n this.name = \"PermissionError\";\n }\n}\n\n/**\n * Format a PlatformApiError as an MCP tool error response.\n */\nexport function formatMcpToolError(error: unknown): {\n content: Array<{ type: \"text\"; text: string }>;\n isError: true;\n} {\n if (error instanceof PlatformApiError) {\n return {\n content: [{ type: \"text\", text: JSON.stringify(error.toMcpError(), null, 2) }],\n isError: true,\n };\n }\n\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ error: message, suggestion: \"Check the error details and retry\" }, null, 2),\n },\n ],\n isError: true,\n };\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { Platform } from \"./types.js\";\nimport { RateLimitError } from \"./errors.js\";\nimport { logger } from \"./logger.js\";\n\ninterface RateLimitConfig {\n /** Max requests per second */\n maxPerSecond: number;\n /** Max requests per minute */\n maxPerMinute: number;\n /** Max retry attempts */\n maxRetries: number;\n}\n\nconst PLATFORM_LIMITS: Record<Platform, RateLimitConfig> = {\n meta: { maxPerSecond: 3, maxPerMinute: 200, maxRetries: 3 },\n};\n\ninterface RequestRecord {\n timestamps: number[];\n}\n\n/**\n * Per-platform rate limiter with exponential backoff.\n * Tracks request timestamps and enforces rate limits before sending.\n */\nexport class RateLimiter {\n private records = new Map<string, RequestRecord>();\n private config: RateLimitConfig;\n\n constructor(private platform: Platform) {\n this.config = PLATFORM_LIMITS[platform];\n }\n\n /**\n * Wait until a request is safe to send, respecting rate limits.\n * Throws RateLimitError if all retries are exhausted.\n */\n async acquire(): Promise<void> {\n const key = this.platform;\n let record = this.records.get(key);\n if (!record) {\n record = { timestamps: [] };\n this.records.set(key, record);\n }\n\n const now = Date.now();\n\n // Clean old timestamps (older than 60s)\n record.timestamps = record.timestamps.filter((t) => now - t < 60_000);\n\n // Check per-second limit\n const lastSecond = record.timestamps.filter((t) => now - t < 1000);\n if (lastSecond.length >= this.config.maxPerSecond) {\n const waitMs = 1000 - (now - lastSecond[0]!) + 50;\n logger.debug(this.platform, `Rate limit: waiting ${waitMs}ms (per-second limit)`);\n await sleep(waitMs);\n }\n\n // Check per-minute limit\n if (record.timestamps.length >= this.config.maxPerMinute) {\n const waitMs = 60_000 - (now - record.timestamps[0]!) + 100;\n logger.warn(this.platform, `Rate limit: waiting ${waitMs}ms (per-minute limit)`);\n await sleep(waitMs);\n }\n\n record.timestamps.push(Date.now());\n }\n\n /**\n * Execute a function with automatic rate limiting and retry on 429.\n */\n async execute<T>(fn: () => Promise<T>): Promise<T> {\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n await this.acquire();\n\n try {\n return await fn();\n } catch (error) {\n if (isRateLimitError(error) && attempt < this.config.maxRetries) {\n const backoff = Math.min(1000 * Math.pow(2, attempt) + jitter(), 30_000);\n logger.warn(\n this.platform,\n `Rate limited (attempt ${attempt + 1}/${this.config.maxRetries}), retrying in ${backoff}ms`,\n );\n await sleep(backoff);\n continue;\n }\n throw error;\n }\n }\n\n throw new RateLimitError(this.platform);\n }\n}\n\nfunction isRateLimitError(error: unknown): boolean {\n if (error instanceof RateLimitError) return true;\n if (error instanceof Error) {\n const msg = error.message.toLowerCase();\n return msg.includes(\"rate limit\") || msg.includes(\"too many requests\");\n }\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction jitter(): number {\n return Math.floor(Math.random() * 500);\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// META GRAPH API CLIENT (READ-ONLY)\n// Meta Graph API client for read-only calls.\n// communication with Meta Marketing API\n// ============================================\n\nimport { logger } from \"../../core/logger.js\";\nimport { RateLimiter } from \"../../core/rate-limiter.js\";\n\n// ============================================\n// CONSTANTS\n// ============================================\n\nconst META_GRAPH_API_BASE = \"https://graph.facebook.com\";\nexport const DEFAULT_API_VERSION = \"v26.0\";\nconst DEFAULT_LIMIT = 500;\n\n/**\n * Keep bearer credentials on Meta's Graph host. Tool-built URLs and Graph\n * paging URLs both pass through this guard before a token is attached.\n */\nexport function assertSafeMetaGraphUrl(rawUrl: string, apiVersion: string): URL {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new Error(\"Meta Graph URL must be an absolute HTTPS URL.\");\n }\n\n if (\n url.protocol !== \"https:\" ||\n url.hostname !== \"graph.facebook.com\" ||\n url.port !== \"\" ||\n url.username !== \"\" ||\n url.password !== \"\"\n ) {\n throw new Error(\"Meta Graph reads are restricted to https://graph.facebook.com.\");\n }\n\n const versionPrefix = `/${apiVersion.replace(/^\\/+|\\/+$/g, \"\")}/`;\n if (!url.pathname.startsWith(versionPrefix) && url.pathname !== versionPrefix.slice(0, -1)) {\n throw new Error(`Meta Graph URL must use the configured API version ${apiVersion}.`);\n }\n\n return url;\n}\n\n// ============================================\n// TYPES (inlined, so there is no external dependency)\n// ============================================\n\nexport interface MetaApiError {\n message: string;\n type: string;\n code: number;\n error_subcode?: number;\n error_user_msg?: string;\n error_user_title?: string;\n fbtrace_id?: string;\n}\n\nexport interface MetaAdAccount {\n id: string;\n account_id: string;\n name: string;\n currency: string;\n timezone_name: string;\n account_status: number;\n}\n\ninterface MetaAdAccountsResponse {\n data: MetaAdAccount[];\n paging?: { cursors?: { before: string; after: string }; next?: string };\n}\n\nexport interface MetaApiRequest {\n type: string;\n fields: string[];\n level: string;\n limit?: number;\n timeRange?: { since: string; until: string };\n datePreset?: string;\n timeIncrement?: number | string;\n breakdowns?: string[];\n actionBreakdowns?: string[];\n attributionWindows?: string[];\n filtering?: Array<{ field: string; operator: string; value: unknown }>;\n}\n\nexport interface InsightRow {\n [key: string]: unknown;\n}\n\ninterface InsightsApiResponse {\n data: InsightRow[];\n paging?: { cursors?: { before: string; after: string }; next?: string };\n}\n\nexport interface QueryPlan {\n requests: MetaApiRequest[];\n joinKeys: string[];\n calculatedMetrics: string[];\n errors: string[];\n warnings: string[];\n}\n\nexport interface QueryDebugInfo {\n requestCount: number;\n rawRequests: MetaApiRequest[];\n calculatedMetrics: string[];\n joinKeys: string[];\n executionTimeMs: number;\n rowCount: number;\n warnings: string[];\n}\n\nexport interface InsightsResponse {\n data: InsightRow[];\n debug: QueryDebugInfo;\n}\n\n// Ad Study types\nexport interface AdStudy {\n id: string;\n name?: string;\n type?: string;\n description?: string;\n created_time?: string;\n start_time?: string;\n end_time?: string;\n results_first_available_date?: string;\n canceled_time?: string;\n observation_end_time?: string;\n cooldown_start_time?: string;\n}\n\nexport interface AdStudyCell {\n id: string;\n name?: string;\n treatment_percentage?: number;\n control_percentage?: number;\n ad_entities_count?: number;\n}\n\nexport interface AdStudyObjective {\n id: string;\n name?: string;\n type?: string;\n is_primary?: boolean;\n last_updated_results?: string;\n results?: unknown;\n custom_attributes?: unknown;\n}\n\ninterface AdStudiesResponse {\n data: AdStudy[];\n paging?: { next?: string };\n}\n\ninterface AdStudyCellsResponse {\n data: AdStudyCell[];\n paging?: { next?: string };\n}\n\ninterface AdStudyObjectivesResponse {\n data: AdStudyObjective[];\n paging?: { next?: string };\n}\n\n// ============================================\n// ERROR HANDLING\n// ============================================\n\nexport class MetaApiException extends Error {\n public readonly code: number;\n public readonly type: string;\n public readonly subcode?: number;\n public readonly userMessage?: string;\n public readonly userTitle?: string;\n public readonly fbtraceId?: string;\n\n constructor(error: MetaApiError) {\n // Include error_user_msg when present, since it carries the actual detail\n const fullMessage = error.error_user_msg\n ? `${error.message}: ${error.error_user_msg}`\n : error.message;\n super(fullMessage);\n this.name = \"MetaApiException\";\n this.code = error.code;\n this.type = error.type;\n this.subcode = error.error_subcode;\n this.userMessage = error.error_user_msg;\n this.userTitle = error.error_user_title;\n this.fbtraceId = error.fbtrace_id;\n }\n\n get isRateLimitError(): boolean {\n return this.code === 613 || this.code === 17;\n }\n\n get isAuthError(): boolean {\n return this.code === 190 || this.code === 102;\n }\n\n get isPermissionError(): boolean {\n return this.code === 200 || this.code === 10;\n }\n\n get isInvalidParameterError(): boolean {\n return this.code === 100;\n }\n\n get suggestion(): string {\n if (this.isRateLimitError) {\n return \"Rate limit exceeded. Wait and retry with exponential backoff.\";\n }\n if (this.isAuthError) {\n return \"Authentication failed. Refresh the access token.\";\n }\n if (this.isPermissionError) {\n return \"Permission denied. Verify ad account access permissions.\";\n }\n if (this.isInvalidParameterError) {\n return \"Invalid parameter. Check metric/breakdown compatibility.\";\n }\n return \"Check the Meta API documentation for error details.\";\n }\n}\n\n// ============================================\n// RESPONSE NORMALIZATION\n// ============================================\n\n/**\n * Normalize an insight row to extract readable values from creative breakdowns.\n * Some breakdowns return objects with id/name, we want to extract the readable name.\n */\nfunction normalizeInsightRow(row: InsightRow): InsightRow {\n const normalized = { ...row };\n\n // For ad_format_asset, if it's an object, extract the name\n if (normalized.ad_format_asset !== undefined) {\n const value = normalized.ad_format_asset as unknown;\n if (typeof value === \"object\" && value !== null) {\n const obj = value as Record<string, unknown>;\n if (\"name\" in obj && typeof obj.name === \"string\") {\n normalized.ad_format_asset = obj.name;\n } else if (\"id\" in obj) {\n normalized.ad_format_asset = String(obj.id);\n }\n }\n }\n\n // Similar normalization for media_type and media_format\n if (normalized.media_type !== undefined) {\n const value = normalized.media_type as unknown;\n if (typeof value === \"object\" && value !== null) {\n const obj = value as Record<string, unknown>;\n if (\"name\" in obj && typeof obj.name === \"string\") {\n normalized.media_type = obj.name;\n } else if (\"value\" in obj && typeof obj.value === \"string\") {\n normalized.media_type = obj.value;\n }\n }\n }\n\n if (normalized.media_format !== undefined) {\n const value = normalized.media_format as unknown;\n if (typeof value === \"object\" && value !== null) {\n const obj = value as Record<string, unknown>;\n if (\"name\" in obj && typeof obj.name === \"string\") {\n normalized.media_format = obj.name;\n } else if (\"value\" in obj && typeof obj.value === \"string\") {\n normalized.media_format = obj.value;\n }\n }\n }\n\n return normalized;\n}\n\n// ============================================\n// API CLIENT (READ-ONLY)\n// ============================================\n\n/**\n * Meta Graph API client, read-only.\n * Accepts an access token via constructor and makes only\n * GET requests against the Meta Marketing API.\n */\nexport class MetaClient {\n private accessToken: string;\n public readonly apiVersion: string;\n private rateLimiter: RateLimiter;\n\n constructor(accessToken: string, apiVersion: string = DEFAULT_API_VERSION) {\n this.accessToken = accessToken;\n this.apiVersion = apiVersion;\n this.rateLimiter = new RateLimiter(\"meta\");\n }\n\n /**\n * Set a new access token.\n */\n setAccessToken(token: string): void {\n this.accessToken = token;\n }\n\n /**\n * Get the current access token (for direct API calls outside MetaClient).\n */\n getAccessToken(): string {\n return this.accessToken;\n }\n\n /**\n * Get the base URL for API requests.\n */\n private getBaseUrl(): string {\n return `${META_GRAPH_API_BASE}/${this.apiVersion}`;\n }\n\n /**\n * Fetch any Meta Graph API URL (with rate limiting and access token).\n * Useful for tools that build URLs directly.\n */\n async fetchUrl(url: string): Promise<Record<string, unknown>> {\n return this.rateLimiter.execute(async () => {\n const safeUrl = assertSafeMetaGraphUrl(url, this.apiVersion);\n safeUrl.searchParams.set(\"access_token\", this.accessToken);\n logger.debug(\"meta\", `fetchUrl: ${safeUrl.origin}${safeUrl.pathname}`);\n const response = await fetch(safeUrl, { redirect: \"error\" });\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({})) as Record<string, unknown>;\n const errorInfo = (errorData.error ?? {}) as Record<string, unknown>;\n throw new MetaApiException({\n message: (errorInfo.message as string) ?? `HTTP ${response.status}`,\n type: (errorInfo.type as string) ?? \"OAuthException\",\n code: (errorInfo.code as number) ?? response.status,\n error_subcode: errorInfo.error_subcode as number | undefined,\n error_user_msg: errorInfo.error_user_msg as string | undefined,\n error_user_title: errorInfo.error_user_title as string | undefined,\n fbtrace_id: errorInfo.fbtrace_id as string | undefined,\n });\n }\n return response.json() as Promise<Record<string, unknown>>;\n });\n }\n\n /**\n * Make a rate-limited GET API request.\n */\n private async request<T>(\n endpoint: string,\n params?: Record<string, string>,\n ): Promise<T> {\n return this.rateLimiter.execute(async () => {\n const url = new URL(`${this.getBaseUrl()}${endpoint}`);\n\n // Add access token\n url.searchParams.append(\"access_token\", this.accessToken);\n\n // Add additional params\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.append(key, value);\n }\n }\n\n logger.debug(\"meta\", `GET ${endpoint}`, {\n fields: params?.fields?.slice(0, 120) ?? \"(none)\",\n });\n\n const response = await fetch(url.toString(), {\n method: \"GET\",\n redirect: \"error\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n\n const data = (await response.json()) as Record<string, unknown>;\n\n // Check for errors\n if (data.error) {\n logger.error(\"meta\", `API error on ${endpoint}`, data.error);\n throw new MetaApiException(data.error as MetaApiError);\n }\n\n return data as T;\n });\n }\n\n // ============================================\n // TOKEN DEBUG\n // ============================================\n\n /**\n * Debug/verify access token.\n */\n async debugToken(): Promise<{\n app_id: string;\n user_id: string;\n expires_at: number;\n is_valid: boolean;\n scopes: string[];\n }> {\n const response = await this.request<{\n data: {\n app_id: string;\n user_id: string;\n expires_at: number;\n is_valid: boolean;\n scopes: string[];\n };\n }>(\"/debug_token\", {\n input_token: this.accessToken,\n });\n\n return response.data;\n }\n\n // ============================================\n // AD ACCOUNT METHODS\n // ============================================\n\n /**\n * Get all ad accounts the user has access to.\n */\n async getAdAccounts(): Promise<MetaAdAccount[]> {\n const allAccounts: MetaAdAccount[] = [];\n let nextUrl: string | undefined;\n\n // First request\n const firstResponse = await this.request<MetaAdAccountsResponse>(\n \"/me/adaccounts\",\n {\n fields:\n \"id,account_id,name,currency,timezone_name,account_status\",\n limit: \"100\",\n },\n );\n\n allAccounts.push(...firstResponse.data);\n nextUrl = firstResponse.paging?.next;\n\n // Paginate through all accounts\n while (nextUrl) {\n const paginatedData = await this.fetchPaginatedUrl<\n MetaAdAccountsResponse & { error?: MetaApiError }\n >(nextUrl);\n\n if (paginatedData.error) {\n throw new MetaApiException(paginatedData.error);\n }\n\n allAccounts.push(...paginatedData.data);\n nextUrl = paginatedData.paging?.next;\n }\n\n return allAccounts;\n }\n\n /**\n * Get a specific ad account.\n */\n async getAdAccount(adAccountId: string): Promise<MetaAdAccount> {\n const accountId = adAccountId.startsWith(\"act_\")\n ? adAccountId\n : `act_${adAccountId}`;\n\n return this.request<MetaAdAccount>(`/${accountId}`, {\n fields:\n \"id,account_id,name,currency,timezone_name,account_status\",\n });\n }\n\n // ============================================\n // INSIGHTS METHODS\n // ============================================\n\n /**\n * Fetch insights for a single API request.\n */\n async fetchInsights(\n adAccountId: string,\n apiRequest: MetaApiRequest,\n ): Promise<InsightRow[]> {\n const accountId = adAccountId.startsWith(\"act_\")\n ? adAccountId\n : `act_${adAccountId}`;\n\n const allRows: InsightRow[] = [];\n let nextUrl: string | undefined;\n\n // Build initial params\n const params: Record<string, string> = {\n fields: apiRequest.fields.join(\",\"),\n level: apiRequest.level,\n limit: String(apiRequest.limit || DEFAULT_LIMIT),\n };\n\n // Time range or preset\n if (apiRequest.timeRange) {\n params.time_range = JSON.stringify({\n since: apiRequest.timeRange.since,\n until: apiRequest.timeRange.until,\n });\n } else if (apiRequest.datePreset) {\n params.date_preset = apiRequest.datePreset;\n }\n\n // Time increment\n if (apiRequest.timeIncrement) {\n params.time_increment = String(apiRequest.timeIncrement);\n }\n\n // Breakdowns - only add if present and non-empty\n if (apiRequest.breakdowns && apiRequest.breakdowns.length > 0) {\n params.breakdowns = apiRequest.breakdowns.join(\",\");\n }\n\n // Action breakdowns - only add if present and non-empty\n if (\n apiRequest.actionBreakdowns &&\n apiRequest.actionBreakdowns.length > 0\n ) {\n params.action_breakdowns = apiRequest.actionBreakdowns.join(\",\");\n }\n\n // Attribution windows: use custom if provided, omit to use account default\n const windows = apiRequest.attributionWindows;\n if (windows === undefined) {\n params.action_attribution_windows =\n \"1d_click,7d_click,28d_click,1d_view,1d_ev\";\n } else if (windows.length > 0) {\n params.action_attribution_windows = windows.join(\",\");\n }\n\n // Filtering (e.g., filter by ad IDs)\n if (apiRequest.filtering && apiRequest.filtering.length > 0) {\n params.filtering = JSON.stringify(apiRequest.filtering);\n }\n\n logger.info(\"meta\", \"Fetching insights\", {\n type: apiRequest.type,\n accountId,\n breakdowns: params.breakdowns || \"(none)\",\n action_breakdowns: params.action_breakdowns || \"(none)\",\n fieldCount: apiRequest.fields.length,\n });\n\n // First request\n const firstResponse = await this.request<InsightsApiResponse>(\n `/${accountId}/insights`,\n params,\n );\n\n allRows.push(...firstResponse.data.map(normalizeInsightRow));\n nextUrl = firstResponse.paging?.next;\n\n // Paginate through all results\n while (nextUrl) {\n const data = await this.fetchPaginatedUrl<\n InsightsApiResponse & { error?: MetaApiError }\n >(nextUrl);\n\n if (data.error) {\n throw new MetaApiException(data.error);\n }\n\n allRows.push(...data.data.map(normalizeInsightRow));\n nextUrl = data.paging?.next;\n }\n\n return allRows;\n }\n\n /**\n * Execute a query plan (multiple API requests) and merge results.\n */\n async executeQueryPlan(\n adAccountId: string,\n plan: QueryPlan,\n mergeResults: (\n results: Record<string, unknown>[][],\n joinKeys: string[],\n ) => Record<string, unknown>[],\n ): Promise<{ data: InsightRow[]; debug: QueryDebugInfo }> {\n const startTime = Date.now();\n\n if (plan.errors.length > 0) {\n throw new Error(`Query plan has errors: ${plan.errors.join(\", \")}`);\n }\n\n // Execute all requests\n const results: InsightRow[][] = [];\n\n for (const request of plan.requests) {\n const rows = await this.fetchInsights(adAccountId, request);\n results.push(rows);\n }\n\n // Merge results\n const mergedData = mergeResults(\n results as Record<string, unknown>[][],\n plan.joinKeys,\n ) as InsightRow[];\n\n const executionTimeMs = Date.now() - startTime;\n\n return {\n data: mergedData,\n debug: {\n requestCount: plan.requests.length,\n rawRequests: plan.requests,\n calculatedMetrics: plan.calculatedMetrics,\n joinKeys: plan.joinKeys,\n executionTimeMs,\n rowCount: mergedData.length,\n warnings: plan.warnings,\n },\n };\n }\n\n // ============================================\n // ENTITY METADATA FETCHING\n // ============================================\n\n /**\n * Fetch metadata fields for multiple entities using batch IDs endpoint.\n * Uses GET /?ids=id1,id2,...&fields=field1,field2 for efficient batch fetching.\n */\n async fetchEntityMetadata(\n entityIds: string[],\n fields: string[],\n ): Promise<Map<string, Record<string, unknown>>> {\n const result = new Map<string, Record<string, unknown>>();\n if (entityIds.length === 0 || fields.length === 0) return result;\n\n const BATCH_SIZE = 50;\n const fieldsStr = fields.join(\",\");\n\n for (let i = 0; i < entityIds.length; i += BATCH_SIZE) {\n const batch = entityIds.slice(i, i + BATCH_SIZE);\n const idsStr = batch.join(\",\");\n\n const data = await this.request<\n Record<string, Record<string, unknown>>\n >(\"\", { ids: idsStr, fields: fieldsStr });\n\n for (const [entityId, metadata] of Object.entries(data)) {\n if (metadata && typeof metadata === \"object\") {\n result.set(entityId, metadata);\n }\n }\n }\n\n return result;\n }\n\n // ============================================\n // ASYNC REPORTING (for large queries)\n // ============================================\n\n /**\n * Check async report status.\n */\n async getAsyncReportStatus(reportRunId: string): Promise<{\n id: string;\n async_status:\n | \"Job Not Started\"\n | \"Job Started\"\n | \"Job Running\"\n | \"Job Completed\"\n | \"Job Failed\"\n | \"Job Skipped\";\n async_percent_completion: number;\n }> {\n return this.request(`/${reportRunId}`, {\n fields: \"id,async_status,async_percent_completion\",\n });\n }\n\n /**\n * Get async report results.\n */\n async getAsyncReportResults(\n reportRunId: string,\n ): Promise<InsightRow[]> {\n const allRows: InsightRow[] = [];\n let nextUrl: string | undefined;\n\n const firstResponse = await this.request<InsightsApiResponse>(\n `/${reportRunId}/insights`,\n { limit: \"500\" },\n );\n\n allRows.push(...firstResponse.data);\n nextUrl = firstResponse.paging?.next;\n\n while (nextUrl) {\n const data = await this.fetchPaginatedUrl<\n InsightsApiResponse & { error?: MetaApiError }\n >(nextUrl);\n\n if (data.error) {\n throw new MetaApiException(data.error);\n }\n\n allRows.push(...data.data);\n nextUrl = data.paging?.next;\n }\n\n return allRows;\n }\n\n /**\n * Wait for async report to complete and get results.\n */\n async waitForAsyncReport(\n reportRunId: string,\n pollIntervalMs: number = 2000,\n maxWaitMs: number = 300000,\n ): Promise<InsightRow[]> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWaitMs) {\n const status = await this.getAsyncReportStatus(reportRunId);\n\n if (status.async_status === \"Job Completed\") {\n return this.getAsyncReportResults(reportRunId);\n }\n\n if (status.async_status === \"Job Failed\") {\n throw new Error(`Async report failed: ${reportRunId}`);\n }\n\n if (status.async_status === \"Job Skipped\") {\n throw new Error(`Async report was skipped: ${reportRunId}`);\n }\n\n // Wait before polling again\n await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));\n }\n\n throw new Error(`Async report timed out after ${maxWaitMs}ms`);\n }\n\n // ============================================\n // EXPERIMENT / AD STUDY METHODS\n // ============================================\n\n /**\n * Fetch all ad studies (experiments).\n * Fetches from three edges to capture all studies:\n * 1. /{ad_account_id}/ad_studies\n * 2. /{business_id}/ad_studies\n * 3. /me/ad_studies\n * Results are deduplicated by study ID.\n */\n async getAdStudies(adAccountId: string): Promise<AdStudy[]> {\n const accountId = adAccountId.startsWith(\"act_\")\n ? adAccountId\n : `act_${adAccountId}`;\n\n const fields = [\n \"id\",\n \"name\",\n \"type\",\n \"description\",\n \"created_time\",\n \"start_time\",\n \"end_time\",\n \"results_first_available_date\",\n \"canceled_time\",\n \"observation_end_time\",\n \"cooldown_start_time\",\n ].join(\",\");\n\n const seenIds = new Set<string>();\n const allStudies: AdStudy[] = [];\n\n // Helper to paginate through an ad_studies edge\n const fetchFromEdge = async (endpoint: string) => {\n try {\n const firstResponse = await this.request<AdStudiesResponse>(\n endpoint,\n { fields, limit: \"100\" },\n );\n\n for (const study of firstResponse.data) {\n if (!seenIds.has(study.id)) {\n seenIds.add(study.id);\n allStudies.push(study);\n }\n }\n\n let nextUrl = firstResponse.paging?.next;\n while (nextUrl) {\n const data = await this.fetchPaginatedUrl<\n AdStudiesResponse & { error?: MetaApiError }\n >(nextUrl);\n if (data.error) break; // Don't throw, just stop paginating\n for (const study of data.data) {\n if (!seenIds.has(study.id)) {\n seenIds.add(study.id);\n allStudies.push(study);\n }\n }\n nextUrl = data.paging?.next;\n }\n } catch (e) {\n // Silently skip if this edge doesn't work\n logger.warn(\n \"meta\",\n `Failed to fetch from ${endpoint}`,\n e instanceof Error ? e.message : e,\n );\n }\n };\n\n // 1. Fetch from ad account edge\n await fetchFromEdge(`/${accountId}/ad_studies`);\n\n // 2. Fetch the business_id from the ad account, then fetch business-level studies\n try {\n const accountInfo = await this.request<{\n business?: { id: string };\n }>(`/${accountId}`, { fields: \"business{id}\" });\n if (accountInfo.business?.id) {\n await fetchFromEdge(`/${accountInfo.business.id}/ad_studies`);\n }\n } catch (e) {\n logger.warn(\n \"meta\",\n \"Failed to fetch business for ad account\",\n e instanceof Error ? e.message : e,\n );\n }\n\n // 3. Also fetch from /me/ad_studies as fallback\n await fetchFromEdge(\"/me/ad_studies\");\n\n return allStudies;\n }\n\n /**\n * Fetch cells for an ad study.\n */\n async getAdStudyCells(studyId: string): Promise<AdStudyCell[]> {\n const fields = [\n \"id\",\n \"name\",\n \"treatment_percentage\",\n \"control_percentage\",\n \"ad_entities_count\",\n ].join(\",\");\n\n const response = await this.request<AdStudyCellsResponse>(\n `/${studyId}/cells`,\n { fields, limit: \"100\" },\n );\n\n return response.data;\n }\n\n /**\n * Fetch objectives for an ad study, optionally with breakdowns.\n */\n async getAdStudyObjectives(\n studyId: string,\n breakdowns?: string[],\n ): Promise<AdStudyObjective[]> {\n const fields = [\n \"id\",\n \"name\",\n \"type\",\n \"is_primary\",\n \"last_updated_results\",\n \"results\",\n \"custom_attributes\",\n ].join(\",\");\n\n const params: Record<string, string> = { fields, limit: \"100\" };\n if (breakdowns && breakdowns.length > 0) {\n params.breakdowns = JSON.stringify(breakdowns);\n }\n\n const response = await this.request<AdStudyObjectivesResponse>(\n `/${studyId}/objectives`,\n params,\n );\n\n return response.data;\n }\n\n /**\n * Fetch objectives with minimal fields (no results, no breakdowns).\n * Fallback when the full objectives endpoint requires whitelist.\n */\n async getAdStudyObjectivesBasic(\n studyId: string,\n ): Promise<AdStudyObjective[]> {\n const fields = [\"id\", \"name\", \"type\", \"is_primary\"].join(\",\");\n\n const response = await this.request<AdStudyObjectivesResponse>(\n `/${studyId}/objectives`,\n { fields, limit: \"100\" },\n );\n\n return response.data;\n }\n\n /**\n * Fetch entity IDs associated with a study cell (campaigns, adsets, or adaccounts).\n */\n async getStudyCellEntities(\n cellId: string,\n entityType: \"campaigns\" | \"adsets\" | \"adaccounts\",\n ): Promise<Array<{ id: string; name?: string }>> {\n const response = await this.request<{\n data: Array<{ id: string; name?: string }>;\n }>(`/${cellId}/${entityType}`, { fields: \"id,name\", limit: \"200\" });\n\n return response.data;\n }\n\n // ============================================\n // READ-ONLY ENTITY METHODS\n // ============================================\n\n /**\n * Get pages the user manages.\n */\n async getPages(): Promise<\n Array<{ id: string; name: string; picture?: string }>\n > {\n const pages: Array<{\n id: string;\n name: string;\n picture?: string;\n }> = [];\n let nextUrl: string | null = null;\n\n const firstPage = await this.request<{\n data: Array<{\n id: string;\n name: string;\n picture?: { data?: { url?: string } };\n }>;\n paging?: { next?: string };\n }>(\"/me/accounts\", {\n fields: \"id,name,picture{url}\",\n limit: \"200\",\n });\n\n for (const p of firstPage.data || []) {\n pages.push({\n id: p.id,\n name: p.name,\n picture: p.picture?.data?.url,\n });\n }\n nextUrl = firstPage.paging?.next || null;\n\n while (nextUrl) {\n const data = await this.fetchPaginatedUrl<{\n data: Array<{\n id: string;\n name: string;\n picture?: { data?: { url?: string } };\n }>;\n paging?: { next?: string };\n error?: MetaApiError;\n }>(nextUrl);\n if (data.error) break;\n for (const p of data.data || []) {\n pages.push({\n id: p.id,\n name: p.name,\n picture: p.picture?.data?.url,\n });\n }\n nextUrl = data.paging?.next || null;\n if (pages.length > 1000) break;\n }\n\n return pages;\n }\n\n /**\n * Get Instagram accounts associated with an ad account.\n */\n async getInstagramAccounts(\n accountId: string,\n ): Promise<\n Array<{ id: string; username: string; profilePicture?: string }>\n > {\n const formattedId = accountId.startsWith(\"act_\")\n ? accountId\n : `act_${accountId}`;\n const accounts: Array<{\n id: string;\n username: string;\n profilePicture?: string;\n }> = [];\n\n try {\n const result = await this.request<{\n data: Array<{\n id: string;\n username?: string;\n profile_picture_url?: string;\n }>;\n paging?: { next?: string };\n }>(`/${formattedId}/instagram_accounts`, {\n fields: \"id,username,profile_picture_url\",\n });\n\n for (const a of result.data || []) {\n accounts.push({\n id: a.id,\n username: a.username || a.id,\n profilePicture: a.profile_picture_url,\n });\n }\n } catch {\n // Fallback: return empty array if endpoint fails\n }\n\n return accounts;\n }\n\n /**\n * Search for campaigns, ad sets, or ads by name using the Management API.\n * Returns ALL entities regardless of spend/activity (unlike Insights API).\n */\n async searchEntities(\n accountId: string,\n entityType: \"campaign\" | \"adset\" | \"ad\",\n options: {\n nameFilter?: string;\n statusFilter?: string[];\n fields?: string[];\n limit?: number;\n } = {},\n ): Promise<Array<Record<string, unknown>>> {\n const formattedId = accountId.startsWith(\"act_\")\n ? accountId\n : `act_${accountId}`;\n const edge =\n entityType === \"campaign\"\n ? \"campaigns\"\n : entityType === \"adset\"\n ? \"adsets\"\n : \"ads\";\n\n // Default fields per entity type\n const defaultFields: Record<string, string[]> = {\n campaign: [\n \"name\",\n \"status\",\n \"effective_status\",\n \"objective\",\n \"daily_budget\",\n \"lifetime_budget\",\n \"created_time\",\n \"updated_time\",\n ],\n adset: [\n \"name\",\n \"status\",\n \"effective_status\",\n \"campaign_id\",\n \"daily_budget\",\n \"lifetime_budget\",\n \"optimization_goal\",\n \"billing_event\",\n \"targeting\",\n \"created_time\",\n ],\n ad: [\n \"name\",\n \"status\",\n \"effective_status\",\n \"adset_id\",\n \"campaign_id\",\n \"creative{id,name,thumbnail_url}\",\n \"created_time\",\n ],\n };\n\n const fields = options.fields?.length\n ? options.fields\n : defaultFields[entityType];\n const limit = options.limit || 50;\n\n const params: Record<string, string> = {\n fields: fields!.join(\",\"),\n limit: String(limit),\n };\n\n // Build filtering\n const filters: Array<{\n field: string;\n operator: string;\n value: string | string[];\n }> = [];\n if (options.nameFilter) {\n filters.push({\n field: \"name\",\n operator: \"CONTAIN\",\n value: options.nameFilter,\n });\n }\n if (options.statusFilter?.length) {\n filters.push({\n field: \"effective_status\",\n operator: \"IN\",\n value: options.statusFilter,\n });\n }\n if (filters.length > 0) {\n params.filtering = JSON.stringify(filters);\n }\n\n const results: Array<Record<string, unknown>> = [];\n let nextUrl: string | null = null;\n\n const firstPage = await this.request<{\n data: Array<Record<string, unknown>>;\n paging?: { next?: string };\n }>(`/${formattedId}/${edge}`, params);\n\n results.push(...(firstPage.data || []));\n nextUrl = firstPage.paging?.next || null;\n\n // Paginate if needed (up to limit)\n while (nextUrl && results.length < limit) {\n const data = await this.fetchPaginatedUrl<{\n data: Array<Record<string, unknown>>;\n paging?: { next?: string };\n error?: MetaApiError;\n }>(nextUrl);\n if (data.error) break;\n results.push(...(data.data || []));\n nextUrl = data.paging?.next || null;\n }\n\n return results.slice(0, limit);\n }\n\n /**\n * Get an ad preview iframe.\n */\n async getAdPreview(\n adId: string,\n format: string = \"DESKTOP_FEED_STANDARD\",\n ): Promise<{ body: string }> {\n const response = await this.request<{\n data: Array<{ body: string }>;\n }>(`/${adId}/previews`, { ad_format: format });\n return response.data[0] || { body: \"\" };\n }\n\n /**\n * Fetch a single campaign by ID.\n */\n async getCampaign(\n campaignId: string,\n ): Promise<Record<string, unknown>> {\n return this.request(`/${campaignId}`, {\n fields:\n \"id,name,status,daily_budget,lifetime_budget,objective,bid_strategy,spend_cap,special_ad_categories\",\n });\n }\n\n /**\n * Fetch a single ad set by ID.\n */\n async getAdSet(adSetId: string): Promise<Record<string, unknown>> {\n return this.request(`/${adSetId}`, {\n fields:\n \"id,name,status,daily_budget,lifetime_budget,bid_amount,billing_event,optimization_goal,targeting,start_time,end_time\",\n });\n }\n\n /**\n * Fetch a single ad by ID.\n */\n async getAd(adId: string): Promise<Record<string, unknown>> {\n return this.request(`/${adId}`, {\n fields:\n \"id,name,status,effective_status,creative{id,name,object_story_spec,call_to_action_type,link_url,image_url,video_id,thumbnail_url}\",\n });\n }\n\n /**\n * Fetch a single ad creative by ID.\n */\n async getCreative(\n creativeId: string,\n ): Promise<Record<string, unknown>> {\n return this.request(`/${creativeId}`, {\n fields:\n \"id,name,object_story_spec,asset_feed_spec,call_to_action_type,link_url,image_url,video_id,thumbnail_url,effective_object_story_id,object_type\",\n });\n }\n\n // ============================================\n // PAGINATION HELPER\n // ============================================\n\n /**\n * Fetch a full paginated URL (used for \"next\" page cursors).\n * Rate-limited via the shared limiter.\n */\n private async fetchPaginatedUrl<T>(url: string): Promise<T> {\n return this.rateLimiter.execute(async () => {\n const safeUrl = assertSafeMetaGraphUrl(url, this.apiVersion);\n const response = await fetch(safeUrl, {\n redirect: \"error\",\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n });\n return (await response.json()) as T;\n });\n }\n}\n\n// ============================================\n// UTILITY FUNCTIONS\n// ============================================\n\n/**\n * Parse a date range from a date preset string.\n */\nexport function getDateRangeFromPreset(\n preset: string,\n): { since: string; until: string } | null {\n const today = new Date();\n const formatDate = (d: Date): string => d.toISOString().split(\"T\")[0]!;\n\n switch (preset) {\n case \"today\":\n return { since: formatDate(today), until: formatDate(today) };\n\n case \"yesterday\": {\n const yesterday = new Date(today);\n yesterday.setDate(yesterday.getDate() - 1);\n return {\n since: formatDate(yesterday),\n until: formatDate(yesterday),\n };\n }\n\n case \"last_7d\": {\n const d = new Date(today);\n d.setDate(d.getDate() - 7);\n return { since: formatDate(d), until: formatDate(today) };\n }\n\n case \"last_14d\": {\n const d = new Date(today);\n d.setDate(d.getDate() - 14);\n return { since: formatDate(d), until: formatDate(today) };\n }\n\n case \"last_28d\": {\n const d = new Date(today);\n d.setDate(d.getDate() - 28);\n return { since: formatDate(d), until: formatDate(today) };\n }\n\n case \"last_30d\": {\n const d = new Date(today);\n d.setDate(d.getDate() - 30);\n return { since: formatDate(d), until: formatDate(today) };\n }\n\n case \"last_90d\": {\n const d = new Date(today);\n d.setDate(d.getDate() - 90);\n return { since: formatDate(d), until: formatDate(today) };\n }\n\n case \"this_month\": {\n const firstOfMonth = new Date(\n today.getFullYear(),\n today.getMonth(),\n 1,\n );\n return {\n since: formatDate(firstOfMonth),\n until: formatDate(today),\n };\n }\n\n case \"last_month\": {\n const firstOfLastMonth = new Date(\n today.getFullYear(),\n today.getMonth() - 1,\n 1,\n );\n const lastOfLastMonth = new Date(\n today.getFullYear(),\n today.getMonth(),\n 0,\n );\n return {\n since: formatDate(firstOfLastMonth),\n until: formatDate(lastOfLastMonth),\n };\n }\n\n default:\n return null;\n }\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { MetricDefinition } from \"./types.js\";\n\n// All breakdowns for general delivery metrics\nconst ALL_BREAKDOWNS = [\n \"age\", \"gender\", \"country\", \"region\", \"dma\",\n \"user_segment_key\", // Audience segment breakdown\n \"publisher_platform\", \"platform_position\", \"device_platform\", \"impression_device\",\n \"image_asset\", \"video_asset\", \"body_asset\", \"title_asset\", \"description_asset\",\n \"call_to_action_asset\", \"link_url_asset\", \"ad_format_asset\",\n \"product_id\", \"product_brand_breakdown\", \"product_category_breakdown\",\n];\n\nconst DEMOGRAPHIC_GEO_BREAKDOWNS = [\"age\", \"gender\", \"country\", \"region\", \"dma\", \"user_segment_key\"];\nconst PLATFORM_BREAKDOWNS = [\"publisher_platform\", \"platform_position\", \"device_platform\", \"impression_device\"];\n\n// Metrics incompatible with hourly breakdowns\nconst NO_HOURLY_BREAKDOWNS = [\n \"hourly_stats_aggregated_by_advertiser_time_zone\",\n \"hourly_stats_aggregated_by_audience_time_zone\",\n];\n\nexport const METRIC_CATALOG: MetricDefinition[] = [\n // ============================================\n // DELIVERY METRICS\n // ============================================\n {\n key: \"impressions\",\n name: \"Impressions\",\n description: \"Total ad displays. The number of times your ad was shown on screen.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"reach\",\n name: \"Reach\",\n description: \"Unique people reached. The number of unique individuals who saw your ad at least once.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: NO_HOURLY_BREAKDOWNS,\n isEstimated: true,\n historicalLimit: 13,\n },\n {\n key: \"frequency\",\n name: \"Frequency\",\n description: \"Average impressions per person. Calculated as impressions / reach.\",\n category: \"delivery\",\n type: \"native\",\n format: \"ratio\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: NO_HOURLY_BREAKDOWNS,\n isEstimated: true,\n },\n {\n key: \"social_reach\",\n name: \"Social Reach\",\n description: \"DEPRECATED (Aug 2024): Number of unique people who saw your ad with social information.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: PLATFORM_BREAKDOWNS,\n isEstimated: true,\n isDeprecated: true,\n },\n {\n key: \"social_impressions\",\n name: \"Social Impressions\",\n description: \"DEPRECATED (Aug 2024): Total impressions that included social context like friends' interactions.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: PLATFORM_BREAKDOWNS,\n isDeprecated: true,\n },\n {\n key: \"full_view_impressions\",\n name: \"Full View Impressions\",\n description: \"Number of times users saw your entire ad content.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"full_view_reach\",\n name: \"Full View Reach\",\n description: \"Unique individuals who saw your entire ad content.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: PLATFORM_BREAKDOWNS,\n isEstimated: true,\n },\n\n // ============================================\n // CLICK METRICS\n // ============================================\n {\n key: \"clicks\",\n name: \"Total Clicks\",\n description: \"All click interactions on your ad including link clicks, profile clicks, and expansions.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"unique_clicks\",\n name: \"Unique Clicks\",\n description: \"Number of distinct individuals who clicked on your ad at least once.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n isEstimated: true,\n },\n {\n key: \"inline_link_clicks\",\n name: \"Inline Link Clicks\",\n description: \"Clicks specifically on links embedded in your ad (CTA button, display URL, etc.).\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"unique_inline_link_clicks\",\n name: \"Unique Link Clicks\",\n description: \"Number of distinct people who clicked on links within your ad.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n isEstimated: true,\n },\n {\n key: \"outbound_clicks\",\n name: \"Outbound Clicks\",\n description: \"Clicks that navigate users away from Meta platforms to external websites or apps.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n apiField: \"outbound_clicks\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n actionType: \"outbound_click\",\n },\n {\n key: \"unique_outbound_clicks\",\n name: \"Unique Outbound Clicks\",\n description: \"Number of distinct people who clicked to leave Meta platforms.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n isEstimated: true,\n },\n {\n key: \"social_clicks\",\n name: \"Social Clicks\",\n description: \"DEPRECATED (Aug 2024): Clicks that occurred when social proof was displayed.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: PLATFORM_BREAKDOWNS,\n isDeprecated: true,\n },\n {\n key: \"instant_experience_clicks_to_open\",\n name: \"Instant Experience Open Clicks\",\n description: \"DEPRECATED: Number of clicks that opened your Instant Experience.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: PLATFORM_BREAKDOWNS,\n isDeprecated: true,\n },\n {\n key: \"instant_experience_clicks_to_start\",\n name: \"Instant Experience Start Clicks\",\n description: \"DEPRECATED: Number of interactions that initiated Instant Experience content.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: PLATFORM_BREAKDOWNS,\n isDeprecated: true,\n },\n\n // ============================================\n // CTR METRICS\n // ============================================\n {\n key: \"ctr\",\n name: \"CTR\",\n description: \"Click-through rate. Percentage of impressions that resulted in any click.\",\n category: \"delivery\",\n type: \"native\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"unique_ctr\",\n name: \"Unique CTR\",\n description: \"Percentage of people reached who clicked at least once.\",\n category: \"delivery\",\n type: \"native\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n isEstimated: true,\n },\n {\n key: \"inline_link_click_ctr\",\n name: \"Link CTR\",\n description: \"Percentage of impressions that resulted in link clicks.\",\n category: \"delivery\",\n type: \"native\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"unique_inline_link_click_ctr\",\n name: \"Unique Link CTR\",\n description: \"Percentage of people reached who clicked on links.\",\n category: \"delivery\",\n type: \"native\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n isEstimated: true,\n },\n {\n key: \"outbound_clicks_ctr\",\n name: \"Outbound CTR\",\n description: \"Percentage of impressions resulting in clicks to external destinations.\",\n category: \"delivery\",\n type: \"native\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // ============================================\n // COST METRICS\n // ============================================\n {\n key: \"spend\",\n name: \"Spend\",\n description: \"Total amount spent. The actual budget consumed by your ads.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n isEstimated: true,\n },\n {\n key: \"cpc\",\n name: \"CPC\",\n description: \"Cost per click. Average cost for each click on your ad.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cpm\",\n name: \"CPM\",\n description: \"Cost per 1,000 impressions. How much you pay for every thousand ad displays.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cpp\",\n name: \"Cost per Person\",\n description: \"DEPRECATED: Cost per 1,000 people reached. Removed from the UI because the metric name was confusing.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n isEstimated: true,\n isDeprecated: true,\n },\n {\n key: \"cost_per_inline_link_click\",\n name: \"Cost per Link Click\",\n description: \"Average cost for each click on links within your ad.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_unique_click\",\n name: \"Cost per Unique Click\",\n description: \"Average cost to acquire each unique person who clicked.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n isEstimated: true,\n },\n {\n key: \"cost_per_unique_inline_link_click\",\n name: \"Cost per Unique Link Click\",\n description: \"Average cost per unique person clicking your links.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n isEstimated: true,\n },\n {\n key: \"cost_per_outbound_click\",\n name: \"Cost per Outbound Click\",\n description: \"Average cost for clicks leading users off Meta platforms.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_inline_post_engagement\",\n name: \"Cost per Engagement\",\n description: \"Average cost for each engagement action on your ad.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_thruplay\",\n name: \"Cost per ThruPlay\",\n description: \"Average cost for each ThruPlay (complete view or 15+ seconds).\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n },\n {\n key: \"social_spend\",\n name: \"Social Spend\",\n description: \"Amount spent on impressions that included social proof.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: PLATFORM_BREAKDOWNS,\n },\n\n // ============================================\n // ENGAGEMENT METRICS\n // ============================================\n {\n key: \"inline_post_engagement\",\n name: \"Post Engagement\",\n description: \"Sum of all engagement actions: reactions, comments, shares, saves, photo views, video plays.\",\n category: \"engagement\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"post_engagement\",\n name: \"Post Engagement (Action)\",\n description: \"All interactions with your ad post: reactions, comments, shares, clicks.\",\n category: \"engagement\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"post_engagement\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"page_engagement\",\n name: \"Page Engagement\",\n description: \"All interactions with your Page from the ad: page likes, post engagements, check-ins.\",\n category: \"engagement\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"page_engagement\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"post_reactions\",\n name: \"Post Reactions\",\n description: \"Total reactions (like, love, wow, haha, sad, angry) on your ad.\",\n category: \"engagement\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"post_reaction\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"comments\",\n name: \"Comments\",\n description: \"Number of comments left on your ad.\",\n category: \"engagement\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"comment\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"shares\",\n name: \"Shares\",\n description: \"Number of times your ad was shared.\",\n category: \"engagement\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"post\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"saves\",\n name: \"Saves\",\n description: \"Number of times users saved your ad for later.\",\n category: \"engagement\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"onsite_conversion.post_save\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"page_likes\",\n name: \"Page Likes\",\n description: \"Number of people who liked/followed your Page from the ad.\",\n category: \"engagement\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"like\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"photo_views\",\n name: \"Photo Views\",\n description: \"Number of times photos in your ad were viewed in enlarged format.\",\n category: \"engagement\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"photo_view\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // ============================================\n // VIDEO METRICS\n // ============================================\n {\n key: \"video_plays\",\n name: \"Video Plays\",\n description: \"Number of times video playback started. Includes both auto-play and click-to-play.\",\n category: \"video\",\n type: \"native\",\n format: \"number\",\n apiField: \"video_play_actions\",\n actionType: \"video_view\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: NO_HOURLY_BREAKDOWNS,\n },\n {\n key: \"video_views_3s\",\n name: \"3-Second Video Views\",\n description: \"Standard video view metric - plays of 3+ seconds or 97% of video if shorter.\",\n category: \"video\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"video_view\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n // DEPRECATED by Meta API as of 2024 - video_10_sec_watched_actions no longer valid\n {\n key: \"video_views_10s\",\n name: \"10-Second Video Views\",\n description: \"DEPRECATED - Videos watched for at least 10 seconds. Use ThruPlay instead.\",\n category: \"video\",\n type: \"native\",\n format: \"number\",\n apiField: \"video_10_sec_watched_actions\",\n actionType: \"video_view\",\n isDeprecated: true,\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: NO_HOURLY_BREAKDOWNS,\n },\n // DEPRECATED by Meta API as of 2024 - video_15_sec_watched_actions no longer valid\n {\n key: \"video_views_15s\",\n name: \"15-Second Video Views\",\n description: \"DEPRECATED - Videos watched for at least 15 seconds. Use ThruPlay instead.\",\n category: \"video\",\n type: \"native\",\n format: \"number\",\n apiField: \"video_15_sec_watched_actions\",\n actionType: \"video_view\",\n isDeprecated: true,\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: NO_HOURLY_BREAKDOWNS,\n },\n {\n key: \"video_views_30s\",\n name: \"30-Second Video Views\",\n description: \"Videos watched for at least 30 seconds, or nearly total length if shorter. Industry standard for meaningful engagement.\",\n category: \"video\",\n type: \"native\",\n format: \"number\",\n apiField: \"video_30_sec_watched_actions\",\n actionType: \"video_view\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: NO_HOURLY_BREAKDOWNS,\n },\n {\n key: \"video_p25\",\n name: \"25% Video Completion\",\n description: \"Videos watched to 25% of total length. First milestone for retention analysis.\",\n category: \"video\",\n type: \"native\",\n format: \"number\",\n apiField: \"video_p25_watched_actions\",\n actionType: \"video_view\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: [...NO_HOURLY_BREAKDOWNS, \"region\"],\n },\n {\n key: \"video_p50\",\n name: \"50% Video Completion\",\n description: \"Videos watched to 50% of length. Shows sustained attention.\",\n category: \"video\",\n type: \"native\",\n format: \"number\",\n apiField: \"video_p50_watched_actions\",\n actionType: \"video_view\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: [...NO_HOURLY_BREAKDOWNS, \"region\"],\n },\n {\n key: \"video_p75\",\n name: \"75% Video Completion\",\n description: \"Videos watched to 75% of length. Strong engagement indicator.\",\n category: \"video\",\n type: \"native\",\n format: \"number\",\n apiField: \"video_p75_watched_actions\",\n actionType: \"video_view\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: [...NO_HOURLY_BREAKDOWNS, \"region\"],\n },\n {\n key: \"video_p95\",\n name: \"95% Video Completion\",\n description: \"Videos watched to 95% of length. Almost complete views.\",\n category: \"video\",\n type: \"native\",\n format: \"number\",\n apiField: \"video_p95_watched_actions\",\n actionType: \"video_view\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: [...NO_HOURLY_BREAKDOWNS, \"region\"],\n },\n {\n key: \"video_p100\",\n name: \"100% Video Completion\",\n description: \"Videos watched to 100%. Your most engaged viewers.\",\n category: \"video\",\n type: \"native\",\n format: \"number\",\n apiField: \"video_p100_watched_actions\",\n actionType: \"video_view\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: [...NO_HOURLY_BREAKDOWNS, \"region\"],\n },\n {\n key: \"thruplay\",\n name: \"ThruPlay\",\n description: \"Complete views OR 15+ seconds for videos over 15 seconds. Meta's standard video completion metric.\",\n category: \"video\",\n type: \"native\",\n format: \"number\",\n apiField: \"video_thruplay_watched_actions\",\n actionType: \"video_view\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: [\"dma\"],\n },\n {\n key: \"video_avg_watch_time\",\n name: \"Average Watch Time\",\n description: \"Average time in seconds viewers spent watching your video.\",\n category: \"video\",\n type: \"native\",\n format: \"duration\",\n apiField: \"video_avg_time_watched_actions\",\n actionType: \"video_view\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: [...NO_HOURLY_BREAKDOWNS, \"region\"],\n },\n {\n key: \"video_total_watch_time\",\n name: \"Total Watch Time\",\n description: \"Cumulative watch time in seconds across all viewers.\",\n category: \"video\",\n type: \"native\",\n format: \"duration\",\n apiField: \"video_time_watched_actions\",\n actionType: \"video_view\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: NO_HOURLY_BREAKDOWNS,\n },\n\n // ============================================\n // CANVAS / INSTANT EXPERIENCE METRICS\n // ============================================\n {\n key: \"canvas_avg_view_percent\",\n name: \"Instant Experience View Percent\",\n description: \"DEPRECATED: Average percentage of Instant Experience content users scrolled through.\",\n category: \"engagement\",\n type: \"native\",\n format: \"percentage\",\n compatibleBreakdowns: PLATFORM_BREAKDOWNS,\n isDeprecated: true,\n },\n {\n key: \"canvas_avg_view_time\",\n name: \"Instant Experience View Time\",\n description: \"DEPRECATED: Mean duration in seconds users spent inside your Instant Experience.\",\n category: \"engagement\",\n type: \"native\",\n format: \"duration\",\n compatibleBreakdowns: PLATFORM_BREAKDOWNS,\n isDeprecated: true,\n },\n\n // ============================================\n // CONVERSION METRICS\n // ============================================\n {\n key: \"actions\",\n name: \"All Actions\",\n description: \"Master array containing all action types attributed to your ads.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"action_values\",\n name: \"Action Values\",\n description: \"Monetary value associated with each action type.\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"conversions\",\n name: \"Conversions\",\n description: \"Tracked conversion events (purchases) from Meta Pixel, Conversions API, or App SDK.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"conversions\",\n actionType: \"offsite_conversion.fb_pixel_purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"conversion_values\",\n name: \"Conversion Values\",\n description: \"Total monetary value of all conversions (purchases).\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"conversion_values\",\n actionType: \"offsite_conversion.fb_pixel_purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_action_type\",\n name: \"Cost per Action\",\n description: \"Average cost for each specific action type.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_conversion\",\n name: \"Cost per Conversion\",\n description: \"Average cost to generate each tracked conversion (purchase).\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n apiField: \"cost_per_conversion\",\n actionType: \"offsite_conversion.fb_pixel_purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // E-commerce specific\n {\n key: \"purchases\",\n name: \"Purchases\",\n description: \"Website purchases tracked via Meta Pixel Purchase event.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"purchase_value\",\n name: \"Purchase Value\",\n description: \"Total revenue from website purchases.\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"action_values\",\n actionType: \"offsite_conversion.fb_pixel_purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"omni_purchases\",\n name: \"Omni Purchases\",\n description: \"All purchases across website, app, and offline channels.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"omni_purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"omni_purchase_value\",\n name: \"Omni Purchase Value\",\n description: \"Total revenue from omnichannel purchases (website, app, offline).\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"action_values\",\n actionType: \"omni_purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"add_to_cart\",\n name: \"Add to Cart\",\n description: \"Products added to shopping cart on your website.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_add_to_cart\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"add_to_cart_value\",\n name: \"Add to Cart Value\",\n description: \"Total value of products added to cart.\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"action_values\",\n actionType: \"offsite_conversion.fb_pixel_add_to_cart\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"initiate_checkout\",\n name: \"Initiate Checkout\",\n description: \"Users who began the checkout process.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_initiate_checkout\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"initiate_checkout_value\",\n name: \"Initiate Checkout Value\",\n description: \"Total value of checkouts initiated.\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"action_values\",\n actionType: \"offsite_conversion.fb_pixel_initiate_checkout\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"view_content\",\n name: \"View Content\",\n description: \"Views of product/content pages on your website.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_view_content\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"view_content_value\",\n name: \"View Content Value\",\n description: \"Total value of content views.\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"action_values\",\n actionType: \"offsite_conversion.fb_pixel_view_content\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"add_payment_info\",\n name: \"Add Payment Info\",\n description: \"Users who added payment details during checkout.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_add_payment_info\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"search\",\n name: \"Search\",\n description: \"Searches performed on your website.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_search\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"add_to_wishlist\",\n name: \"Add to Wishlist\",\n description: \"Products saved to wishlist.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_add_to_wishlist\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // Lead specific\n {\n key: \"leads\",\n name: \"Leads\",\n description: \"Website leads tracked via Meta Pixel on your website.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_lead\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"onsite_leads\",\n name: \"On-Facebook Leads\",\n description: \"Leads from Facebook/Instagram lead forms completed without leaving the platform.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"onsite_conversion.lead_grouped\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"leadgen_grouped\",\n name: \"Leadgen Grouped\",\n description: \"Combined leads from Messenger conversations and Instant Forms.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"leadgen_grouped\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"complete_registration\",\n name: \"Complete Registration\",\n description: \"Completed account registrations on your website.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_complete_registration\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"contact\",\n name: \"Contact\",\n description: \"Contact form submissions on your website.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_contact\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"schedule\",\n name: \"Schedule\",\n description: \"Appointments scheduled on your website.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_schedule\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"start_trial\",\n name: \"Start Trial\",\n description: \"Free trial or demo registrations.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_start_trial\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"subscribe\",\n name: \"Subscribe\",\n description: \"Recurring subscription signups.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_subscribe\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // Messaging\n {\n key: \"messaging_conversations_started\",\n name: \"Conversations Started\",\n description: \"New Messenger or Instagram Direct conversations started within 7 days.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"onsite_conversion.messaging_conversation_started_7d\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"messaging_first_reply\",\n name: \"First Reply\",\n description: \"Users who sent their first message in a conversation.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"onsite_conversion.messaging_first_reply\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // Link clicks\n {\n key: \"link_clicks\",\n name: \"Link Clicks\",\n description: \"Clicks on the main destination link of your ad.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"link_click\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"landing_page_views\",\n name: \"Landing Page Views\",\n description: \"Number of times the landing page loaded after link click.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"landing_page_view\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // App metrics\n {\n key: \"app_installs\",\n name: \"App Installs\",\n description: \"Number of app installs attributed to your ads.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"omni_app_install\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"app_purchases\",\n name: \"In-App Purchases\",\n description: \"Completed transactions within your mobile app.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"app_custom_event.fb_mobile_purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"app_purchase_value\",\n name: \"In-App Purchase Value\",\n description: \"Total revenue from in-app purchases tracked via App SDK.\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"action_values\",\n actionType: \"app_custom_event.fb_mobile_purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // Website-specific metrics (pixel only, excludes app and shop)\n {\n key: \"website_purchases\",\n name: \"Website Purchases\",\n description: \"Purchases tracked via Meta Pixel/CAPI only (excludes in-app and shop).\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"website_purchase_value\",\n name: \"Website Purchase Value\",\n description: \"Revenue from website purchases only (Pixel/CAPI, excludes in-app and shop).\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"action_values\",\n actionType: \"offsite_conversion.fb_pixel_purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // ============================================\n // ROAS METRICS\n // ============================================\n {\n key: \"purchase_roas\",\n name: \"ROAS\",\n description: \"Return on ad spend. Revenue per dollar spent. ROAS of 3.0 = $3 revenue per $1 spent.\",\n category: \"conversion\",\n type: \"native\",\n format: \"ratio\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"website_purchase_roas\",\n name: \"Website ROAS\",\n description: \"ROAS from website purchases tracked via Pixel/CAPI only.\",\n category: \"conversion\",\n type: \"native\",\n format: \"ratio\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"mobile_app_purchase_roas\",\n name: \"App ROAS\",\n description: \"ROAS from mobile app purchases tracked via App SDK.\",\n category: \"conversion\",\n type: \"native\",\n format: \"ratio\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // ============================================\n // RESULTS METRICS\n // ============================================\n {\n key: \"results\",\n name: \"Results\",\n description: \"Number of results based on your campaign objective.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"result_rate\",\n name: \"Result Rate\",\n description: \"Percentage of impressions that achieved your objective result.\",\n category: \"conversion\",\n type: \"native\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_result\",\n name: \"Cost per Result\",\n description: \"Average cost for each result based on your objective.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // ============================================\n // QUALITY METRICS\n // ============================================\n {\n key: \"quality_ranking\",\n name: \"Quality Ranking\",\n description: \"How your ad's perceived quality compared to competing ads.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: [],\n },\n {\n key: \"engagement_rate_ranking\",\n name: \"Engagement Ranking\",\n description: \"Your ad's expected engagement rate compared to competitors.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: [],\n },\n {\n key: \"conversion_rate_ranking\",\n name: \"Conversion Ranking\",\n description: \"Expected conversion rate compared to competitors with same optimization goal.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: [],\n },\n\n // ============================================\n // AWARENESS METRICS\n // ============================================\n {\n key: \"estimated_ad_recall_rate\",\n name: \"Ad Recall Rate\",\n description: \"Estimated percentage of people who would remember seeing your ad if asked within 2 days.\",\n category: \"delivery\",\n type: \"native\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n incompatibleBreakdowns: [\"dma\"],\n isEstimated: true,\n },\n {\n key: \"estimated_ad_recallers\",\n name: \"Estimated Ad Recallers\",\n description: \"Estimated number of people who would recall seeing your ad.\",\n category: \"delivery\",\n type: \"native\",\n format: \"number\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n isEstimated: true,\n },\n {\n key: \"cost_per_estimated_ad_recallers\",\n name: \"Cost per Ad Recaller\",\n description: \"How much you paid for each person estimated to remember your ad.\",\n category: \"cost\",\n type: \"native\",\n format: \"currency\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n isEstimated: true,\n },\n\n // ============================================\n // CALCULATED METRICS (derived client-side)\n // ============================================\n {\n key: \"hookRate\",\n name: \"Hook Rate\",\n description: \"Percentage of impressions that resulted in 3-second video views. Measures if the first 3 seconds grab attention.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"impressions\", \"actions\"],\n },\n {\n key: \"thumbstopRate\",\n name: \"Thumbstop Rate\",\n description: \"Percentage of impressions that resulted in video plays. How many people stopped scrolling to watch.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"impressions\", \"video_play_actions\"],\n },\n {\n key: \"holdRate\",\n name: \"Hold Rate\",\n description: \"Percentage of impressions that resulted in a ThruPlay. Overall video engagement.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"impressions\", \"video_thruplay_watched_actions\"],\n },\n {\n key: \"completionRate\",\n name: \"Completion Rate\",\n description: \"Percentage of 25% viewers who watched to 100%. Full video watch-through rate.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"video_p25_watched_actions\", \"video_p100_watched_actions\"],\n },\n {\n key: \"clickToAtcRatio\",\n name: \"Click to Add to Cart Ratio\",\n description: \"Percentage of link clicks that resulted in add to cart. Landing page effectiveness.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"atcToPurchaseRatio\",\n name: \"Add to Cart to Purchase Ratio\",\n description: \"Percentage of add to carts that converted to purchase. Checkout funnel efficiency.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"clickToPurchaseRatio\",\n name: \"Click to Purchase Ratio\",\n description: \"Percentage of link clicks that resulted in purchase. Overall conversion efficiency.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"aov\",\n name: \"Average Order Value\",\n description: \"Average Order Value. Average basket size calculated as purchase value / purchase count.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\", \"action_values\"],\n },\n {\n key: \"costPerAtc\",\n name: \"Cost per Add to Cart\",\n description: \"DEPRECATED: Duplicate of cost_per_add_to_cart. Use cost_per_add_to_cart instead.\",\n category: \"cost\",\n type: \"calculated\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"spend\", \"actions\"],\n isDeprecated: true,\n },\n {\n key: \"engagementRate\",\n name: \"Engagement Rate\",\n description: \"Percentage of impressions that resulted in any engagement. Content resonance.\",\n category: \"engagement\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"impressions\", \"inline_post_engagement\"],\n },\n\n // ============================================\n // NEW NATIVE METRICS - FROM COMPETITOR ANALYSIS\n // ============================================\n\n // Additional conversion metrics\n {\n key: \"find_location\",\n name: \"Find Location\",\n description: \"Number of times users clicked to find a store location.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"offsite_conversion.fb_pixel_find_location\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"subscribe_value\",\n name: \"Subscription Value\",\n description: \"Total revenue from subscription signups.\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"action_values\",\n actionType: \"offsite_conversion.fb_pixel_subscribe\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"leads_value\",\n name: \"Leads Value\",\n description: \"Total monetary value of leads generated.\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"action_values\",\n actionType: \"offsite_conversion.fb_pixel_lead\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"start_trial_value\",\n name: \"Trial Started Value\",\n description: \"Total value of trial subscriptions started.\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"action_values\",\n actionType: \"offsite_conversion.fb_pixel_start_trial\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // App-specific metrics\n {\n key: \"app_add_to_cart\",\n name: \"App Add to Cart\",\n description: \"Products added to cart within your mobile app.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"app_custom_event.fb_mobile_add_to_cart\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"app_initiate_checkout\",\n name: \"App Initiate Checkout\",\n description: \"Users who began checkout within your mobile app.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"app_custom_event.fb_mobile_initiated_checkout\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"app_complete_registration\",\n name: \"App Registration\",\n description: \"Completed account registrations within your mobile app.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"app_custom_event.fb_mobile_complete_registration\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"achievement_unlocked\",\n name: \"Achievement Unlocked\",\n description: \"In-app achievements unlocked by users.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"app_custom_event.fb_mobile_achievement_unlocked\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"credit_spent\",\n name: \"Credit Spent\",\n description: \"In-app credit spending events.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"app_custom_event.fb_mobile_spent_credits\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"app_start_trial\",\n name: \"App Trial Started\",\n description: \"Free trial starts within your mobile app.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"app_custom_event.fb_mobile_start_trial\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"app_subscribe\",\n name: \"App Subscribe\",\n description: \"Subscription purchases within your mobile app.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"app_custom_event.fb_mobile_subscribe\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // Shop / On-platform conversions\n {\n key: \"shop_purchases\",\n name: \"Shop Purchases\",\n description: \"Purchases completed within Facebook/Instagram Shops.\",\n category: \"conversion\",\n type: \"native\",\n format: \"number\",\n apiField: \"actions\",\n actionType: \"onsite_conversion.purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"shop_purchase_value\",\n name: \"Shop Purchase Value\",\n description: \"Total revenue from Facebook/Instagram Shop purchases.\",\n category: \"conversion\",\n type: \"native\",\n format: \"currency\",\n apiField: \"action_values\",\n actionType: \"onsite_conversion.purchase\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // Cost per action metrics\n // ============================================\n // CALCULATED COST PER METRICS\n // These are calculated as spend / count to ensure values even when\n // cost_per_action_type array doesn't include the action type\n // ============================================\n {\n key: \"cost_per_page_like\",\n name: \"Cost per Page Like\",\n description: \"Average cost to acquire each new Page like/follow. Calculated as spend / page_likes.\",\n category: \"cost\",\n type: \"calculated\",\n format: \"currency\",\n requiredFields: [\"spend\", \"page_likes\"],\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_messaging_conversation\",\n name: \"Cost per Conversation\",\n description: \"Average cost per messaging conversation started. Calculated as spend / conversations.\",\n category: \"cost\",\n type: \"calculated\",\n format: \"currency\",\n requiredFields: [\"spend\", \"messaging_conversations_started\"],\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_lead\",\n name: \"Cost per Lead\",\n description: \"Average cost to generate each lead. Calculated as spend / leads.\",\n category: \"cost\",\n type: \"calculated\",\n format: \"currency\",\n requiredFields: [\"spend\", \"leads\"],\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_purchase\",\n name: \"Cost per Purchase\",\n description: \"Average cost to generate each purchase. Calculated as spend / purchases.\",\n category: \"cost\",\n type: \"calculated\",\n format: \"currency\",\n requiredFields: [\"spend\", \"purchases\"],\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_add_to_cart\",\n name: \"Cost per Add to Cart\",\n description: \"Average cost for each add to cart action. Calculated as spend / add_to_cart.\",\n category: \"cost\",\n type: \"calculated\",\n format: \"currency\",\n requiredFields: [\"spend\", \"add_to_cart\"],\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_initiate_checkout\",\n name: \"Cost per Checkout\",\n description: \"Average cost for each checkout initiation. Calculated as spend / checkouts.\",\n category: \"cost\",\n type: \"calculated\",\n format: \"currency\",\n requiredFields: [\"spend\", \"initiate_checkout\"],\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_app_install\",\n name: \"Cost per Install\",\n description: \"Average cost to generate each app install. Calculated as spend / installs.\",\n category: \"cost\",\n type: \"calculated\",\n format: \"currency\",\n requiredFields: [\"spend\", \"app_installs\"],\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_landing_page_view\",\n name: \"Cost per Landing Page View\",\n description: \"Average cost for each landing page view. Calculated as spend / landing_page_views.\",\n category: \"cost\",\n type: \"calculated\",\n format: \"currency\",\n requiredFields: [\"spend\", \"landing_page_views\"],\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n {\n key: \"cost_per_3s_video_view\",\n name: \"Cost per 3s Video View\",\n description: \"Average cost for each 3-second video view. Calculated as spend / video_views_3s.\",\n category: \"cost\",\n type: \"calculated\",\n format: \"currency\",\n requiredFields: [\"spend\", \"video_views_3s\"],\n compatibleBreakdowns: ALL_BREAKDOWNS,\n },\n\n // ============================================\n // NEW CALCULATED METRICS - FROM COMPETITOR ANALYSIS\n // ============================================\n\n // Share/Percentage metrics (relative to total)\n {\n key: \"spendShare\",\n name: \"% Spend\",\n description: \"Percentage of total spend represented by this row. Useful for budget allocation analysis.\",\n category: \"cost\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"spend\"],\n },\n {\n key: \"purchaseValueShare\",\n name: \"% Purchase Value\",\n description: \"Percentage of total revenue represented by this row. Shows revenue contribution.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"action_values\"],\n },\n\n // ROAS variants\n {\n key: \"subscriptionRoas\",\n name: \"Subscription ROAS\",\n description: \"Return on ad spend from subscription revenue only.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"ratio\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"spend\", \"action_values\"],\n },\n {\n key: \"leadsRoas\",\n name: \"Leads ROAS\",\n description: \"Return on ad spend from lead value only.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"ratio\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"spend\", \"action_values\"],\n },\n\n // Per 1,000 impressions metrics\n {\n key: \"purchasesPerMille\",\n name: \"Purchases per 1K Impressions\",\n description: \"Number of purchases for every 1,000 impressions. Direct conversion efficiency.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"number\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"impressions\", \"actions\"],\n },\n {\n key: \"revenuePerMille\",\n name: \"Revenue per 1K Impressions\",\n description: \"Revenue generated for every 1,000 impressions.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"impressions\", \"action_values\"],\n },\n {\n key: \"installsPerMille\",\n name: \"Installs per 1K Impressions\",\n description: \"App installs for every 1,000 impressions.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"number\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"impressions\", \"actions\"],\n },\n {\n key: \"leadsPerMille\",\n name: \"Leads per 1K Impressions\",\n description: \"Leads generated for every 1,000 impressions.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"number\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"impressions\", \"actions\"],\n },\n\n // Engagement rate variants\n {\n key: \"impressionsToLeadsRatio\",\n name: \"Impressions to Leads Ratio\",\n description: \"Percentage of impressions that resulted in leads.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"impressions\", \"actions\"],\n },\n {\n key: \"likeRate\",\n name: \"Like Rate\",\n description: \"Percentage of impressions that resulted in post reactions (likes, love, etc.).\",\n category: \"engagement\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"impressions\", \"actions\"],\n },\n {\n key: \"commentRate\",\n name: \"Comment Rate\",\n description: \"Percentage of impressions that resulted in comments.\",\n category: \"engagement\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"impressions\", \"actions\"],\n },\n {\n key: \"postShareRatio\",\n name: \"Post Share Ratio (PSR)\",\n description: \"Percentage of impressions that resulted in shares. Viral potential indicator.\",\n category: \"engagement\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"impressions\", \"actions\"],\n },\n {\n key: \"postEngagementsExclVideo\",\n name: \"Engagements (exc. Video)\",\n description: \"Post engagements excluding video plays. Pure social engagement.\",\n category: \"engagement\",\n type: \"calculated\",\n format: \"number\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"inline_post_engagement\", \"actions\"],\n },\n {\n key: \"postEngagementRateExclVideo\",\n name: \"Engagement Rate (exc. Video)\",\n description: \"Engagement rate excluding video plays from engagement count.\",\n category: \"engagement\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"impressions\", \"inline_post_engagement\", \"actions\"],\n },\n\n // Video retention metrics\n {\n key: \"video15sTo3sRetention\",\n name: \"15s/3s Video Retention\",\n description: \"Percentage of 3-second viewers who continued to 15 seconds.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"video_15_sec_watched_actions\", \"actions\"],\n },\n {\n key: \"thruplayCtr\",\n name: \"ThruPlay Rate\",\n description: \"Percentage of impressions that resulted in a ThruPlay (15+ seconds or complete view). Measures video engagement quality.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"impressions\", \"video_thruplay_watched_actions\"],\n },\n {\n key: \"firstFrameRetention\",\n name: \"First Frame Retention\",\n description: \"Percentage of impressions where video started playing. Same as thumbstop rate.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"impressions\", \"video_play_actions\"],\n },\n {\n key: \"thumbstopClickRate\",\n name: \"Thumbstop Click Rate\",\n description: \"Percentage of video plays that resulted in link clicks.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"video_play_actions\", \"actions\"],\n },\n {\n key: \"costPerMinuteViewed\",\n name: \"Cost per Minute Viewed\",\n description: \"Average cost for each minute of video watched. Calculated as spend / (total_watch_time / 60).\",\n category: \"cost\",\n type: \"calculated\",\n format: \"currency\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"spend\", \"video_total_watch_time\"],\n },\n\n // Video completion rate variants\n {\n key: \"videoP25Rate\",\n name: \"25% Video View Rate\",\n description: \"Percentage of impressions that resulted in 25% video completion.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"impressions\", \"video_p25_watched_actions\"],\n },\n {\n key: \"videoP50Rate\",\n name: \"50% Video View Rate\",\n description: \"Percentage of impressions that resulted in 50% video completion.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"impressions\", \"video_p50_watched_actions\"],\n },\n {\n key: \"videoP75Rate\",\n name: \"75% Video View Rate\",\n description: \"Percentage of impressions that resulted in 75% video completion.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"impressions\", \"video_p75_watched_actions\"],\n },\n {\n key: \"videoP95Rate\",\n name: \"95% Video View Rate\",\n description: \"Percentage of impressions that resulted in 95% video completion.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"impressions\", \"video_p95_watched_actions\"],\n },\n {\n key: \"videoP100Rate\",\n name: \"100% Video View Rate\",\n description: \"Percentage of impressions that resulted in complete video views.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"impressions\", \"video_p100_watched_actions\"],\n },\n\n // Video drop-off metrics\n {\n key: \"dropOffP25ToP50\",\n name: \"Drop-off 25% to 50%\",\n description: \"Percentage of viewers lost between 25% and 50% mark.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"video_p25_watched_actions\", \"video_p50_watched_actions\"],\n },\n {\n key: \"dropOffP50ToP75\",\n name: \"Drop-off 50% to 75%\",\n description: \"Percentage of viewers lost between 50% and 75% mark.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"video_p50_watched_actions\", \"video_p75_watched_actions\"],\n },\n {\n key: \"dropOffP75ToP95\",\n name: \"Drop-off 75% to 95%\",\n description: \"Percentage of viewers lost between 75% and 95% mark.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"video_p75_watched_actions\", \"video_p95_watched_actions\"],\n },\n {\n key: \"dropOffP75ToP100\",\n name: \"Drop-off 75% to 100%\",\n description: \"Percentage of viewers lost between 75% mark and completion.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"video_p75_watched_actions\", \"video_p100_watched_actions\"],\n },\n {\n key: \"dropOffP95ToP100\",\n name: \"Drop-off 95% to 100%\",\n description: \"Percentage of viewers lost between 95% mark and completion.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"video_p95_watched_actions\", \"video_p100_watched_actions\"],\n },\n {\n key: \"sustainRate\",\n name: \"Sustain Rate\",\n description: \"Percentage of viewers who watched 25% that continued to 95%. Measures sustained attention.\",\n category: \"video\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: [...DEMOGRAPHIC_GEO_BREAKDOWNS, ...PLATFORM_BREAKDOWNS],\n requiredFields: [\"video_p25_watched_actions\", \"video_p95_watched_actions\"],\n },\n\n // Click quality & funnel metrics\n {\n key: \"clickQuality\",\n name: \"Click Quality\",\n description: \"Conversion rate from clicks. Higher = better quality traffic.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"clicks\", \"actions\"],\n },\n {\n key: \"averageLeadsValue\",\n name: \"Average Lead Value\",\n description: \"Average monetary value per lead generated.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\", \"action_values\"],\n },\n\n // Click to conversion ratios\n {\n key: \"clickToAppInstallRatio\",\n name: \"Click to Install Ratio\",\n description: \"Percentage of clicks that resulted in app installs.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"clickToLeadsRatio\",\n name: \"Click to Leads Ratio\",\n description: \"Percentage of clicks that resulted in leads.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"clickToSubscriptionsRatio\",\n name: \"Click to Subscription Ratio\",\n description: \"Percentage of clicks that resulted in subscriptions.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"clickToRegistrationsRatio\",\n name: \"Click to Registration Ratio\",\n description: \"Percentage of clicks that resulted in completed registrations.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"clickToTrialStartedRatio\",\n name: \"Click to Trial Ratio\",\n description: \"Percentage of clicks that resulted in trial starts.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"leadsToSubscriptionsRatio\",\n name: \"Leads to Subscription Ratio\",\n description: \"Percentage of leads that converted to subscriptions.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"clickToContactRatio\",\n name: \"Click to Contact Ratio\",\n description: \"Percentage of clicks that resulted in contact form submissions.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"clickToFindLocationRatio\",\n name: \"Click to Find Location Ratio\",\n description: \"Percentage of clicks that resulted in store locator searches.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"clickToAppointmentsRatio\",\n name: \"Click to Appointment Ratio\",\n description: \"Percentage of clicks that resulted in scheduled appointments.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"clickToInitiateCheckoutRatio\",\n name: \"Click to Checkout Ratio\",\n description: \"Percentage of clicks that resulted in checkout initiations.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"clickToWebsitePurchaseRatio\",\n name: \"Click to Website Purchase Ratio\",\n description: \"Percentage of clicks that resulted in website purchases.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n\n // App funnel metrics\n {\n key: \"installToTrialRate\",\n name: \"Install to Trial Rate\",\n description: \"Percentage of app installs that started a trial.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"installToPurchaseRatio\",\n name: \"Install to Purchase Ratio\",\n description: \"Percentage of app installs that made a purchase.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"installToRegistrationRate\",\n name: \"Install to Registration Rate\",\n description: \"Percentage of app installs that completed registration.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"registrationToAchievementRate\",\n name: \"Registration to Achievement Rate\",\n description: \"Percentage of registrations that unlocked an achievement.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"achievementToPurchaseRate\",\n name: \"Achievement to Purchase Rate\",\n description: \"Percentage of achievement unlocks that resulted in purchase.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n\n // E-commerce funnel metrics\n {\n key: \"viewContentRatio\",\n name: \"View Content Ratio\",\n description: \"Percentage of impressions that resulted in content views.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"impressions\", \"actions\"],\n },\n {\n key: \"viewContentToAtcRatio\",\n name: \"View Content to Add to Cart Ratio\",\n description: \"Percentage of content views that resulted in add to cart.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n {\n key: \"checkoutToPurchaseRatio\",\n name: \"Checkout to Purchase Ratio\",\n description: \"Percentage of checkout initiations that completed purchase.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"percentage\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\"],\n },\n\n // AOV variants\n {\n key: \"aovWebsite\",\n name: \"Average Order Value (Website)\",\n description: \"Average order value from website purchases only.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\", \"action_values\"],\n },\n {\n key: \"aovShop\",\n name: \"Average Order Value (Shop)\",\n description: \"Average order value from Facebook/Instagram Shop purchases.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\", \"action_values\"],\n },\n {\n key: \"aovApp\",\n name: \"Average Order Value (In-App)\",\n description: \"Average order value from mobile app purchases.\",\n category: \"conversion\",\n type: \"calculated\",\n format: \"currency\",\n compatibleBreakdowns: ALL_BREAKDOWNS,\n requiredFields: [\"actions\", \"action_values\"],\n },\n];\n\n// Helper to get metric by key\n// Also searches by apiField as a fallback for robustness\nexport function getMetricByKey(key: string): MetricDefinition | undefined {\n // First try to find by key (primary lookup)\n const byKey = METRIC_CATALOG.find((m) => m.key === key);\n if (byKey) return byKey;\n\n // Fallback: try to find by apiField (in case apiField was used instead of key)\n return METRIC_CATALOG.find((m) => m.apiField === key);\n}\n\n// Helper to get metrics by category\nexport function getMetricsByCategory(category: MetricDefinition[\"category\"]): MetricDefinition[] {\n return METRIC_CATALOG.filter((m) => m.category === category);\n}\n\n// Helper to get native metrics only\nexport function getNativeMetrics(): MetricDefinition[] {\n return METRIC_CATALOG.filter((m) => m.type === \"native\");\n}\n\n// Helper to get calculated metrics only\nexport function getCalculatedMetrics(): MetricDefinition[] {\n return METRIC_CATALOG.filter((m) => m.type === \"calculated\");\n}\n\n// Helper to check if a metric is compatible with a breakdown\nexport function isMetricCompatibleWithBreakdown(metricKey: string, breakdownKey: string): boolean {\n const metric = getMetricByKey(metricKey);\n if (!metric) return false;\n\n if (metric.incompatibleBreakdowns?.includes(breakdownKey)) {\n return false;\n }\n\n return metric.compatibleBreakdowns.includes(breakdownKey) || metric.compatibleBreakdowns.length === 0;\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// META MARKETING API BREAKDOWN CATALOG\n// Complete breakdown definitions for Meta insights reporting\n// ============================================\n\nimport type { BreakdownDefinition, BreakdownCategory } from \"./types.js\";\nexport type { BreakdownDefinition, BreakdownCategory } from \"./types.js\";\n\n/**\n * Complete catalog of Meta Marketing API breakdowns (dimensions)\n * Based on Meta Marketing API v26.0\n */\nexport const BREAKDOWN_CATALOG: BreakdownDefinition[] = [\n // ============================================\n // DEMOGRAPHIC BREAKDOWNS\n // ============================================\n {\n key: \"age\",\n name: \"Age\",\n description:\n \"Age group segmentation. Splits performance by age bracket: 13-17, 18-24, 25-34, 35-44, 45-54, 55-64, 65+. Essential for understanding which generations respond to your ads.\",\n category: \"demographic\",\n possibleValues: [\n \"13-17\",\n \"18-24\",\n \"25-34\",\n \"35-44\",\n \"45-54\",\n \"55-64\",\n \"65+\",\n ],\n historicalLimit: 13, // months with reach metrics\n },\n {\n key: \"gender\",\n name: \"Gender\",\n description:\n \"Gender segmentation. Splits performance by gender: male, female, unknown. 'Unknown' includes users who haven't specified or non-binary.\",\n category: \"demographic\",\n possibleValues: [\"male\", \"female\", \"unknown\"],\n historicalLimit: 13,\n },\n\n // ============================================\n // AUDIENCE BREAKDOWNS\n // ============================================\n {\n key: \"user_segment_key\",\n name: \"User Segment (ASC)\",\n description:\n \"User segment (new, existing) of Advantage+ Shopping Campaigns (ASC). Existing user is specified by the custom audience in ASC settings. Also works with other Advantage+ campaigns showing audience relationship: new audience, engaged audience, existing customers.\",\n category: \"audience\",\n possibleValues: [\n \"new\",\n \"existing\",\n \"new_audience\",\n \"engaged_audience\",\n \"existing_customers\",\n \"unknown\",\n ],\n },\n\n // ============================================\n // GEOGRAPHIC BREAKDOWNS\n // ============================================\n {\n key: \"country\",\n name: \"Country\",\n description:\n \"Country-level performance using ISO codes (US, FR, GB, DE, etc.). Essential for international campaigns and market analysis.\",\n category: \"geographic\",\n historicalLimit: 13,\n },\n {\n key: \"region\",\n name: \"Region\",\n description:\n \"Region/state-level performance. More granular than country. Useful for localized campaigns and regional promotions.\",\n category: \"geographic\",\n historicalLimit: 13,\n blocksOffsiteMetrics: true,\n },\n {\n key: \"dma\",\n name: \"DMA (Designated Market Area)\",\n description:\n \"US-only Nielsen market zones. Used for local TV-aligned advertising and regional US campaigns.\",\n category: \"geographic\",\n historicalLimit: 13,\n blocksOffsiteMetrics: true,\n },\n\n // ============================================\n // PLATFORM & PLACEMENT BREAKDOWNS\n // ============================================\n {\n key: \"publisher_platform\",\n name: \"Publisher Platform\",\n description:\n \"Which Meta platform showed your ad: facebook, instagram, audience_network, messenger, whatsapp. Compare cross-platform performance.\",\n category: \"platform\",\n possibleValues: [\n \"facebook\",\n \"instagram\",\n \"audience_network\",\n \"messenger\",\n \"whatsapp\",\n ],\n },\n {\n key: \"platform_position\",\n name: \"Platform Position\",\n description:\n \"Specific placement within platforms: feed, story, reels, explore, search, marketplace, video_feeds, instream_video, right_hand_column, instant_article, etc.\",\n category: \"platform\",\n possibleValues: [\n \"feed\",\n \"story\",\n \"reels\",\n \"explore\",\n \"search\",\n \"marketplace\",\n \"video_feeds\",\n \"instream_video\",\n \"right_hand_column\",\n \"instant_article\",\n \"rewarded_video\",\n \"an_classic\",\n \"biz_disco_feed\",\n \"facebook_reels_overlay\",\n \"profile_feed\",\n ],\n requiresCombination: [\"publisher_platform\"],\n },\n {\n key: \"device_platform\",\n name: \"Device Platform\",\n description:\n \"Performance by device type: mobile, desktop, tablet. Understand device preferences of your audience.\",\n category: \"platform\",\n possibleValues: [\"mobile\", \"desktop\", \"tablet\"],\n },\n {\n key: \"impression_device\",\n name: \"Impression Device\",\n description:\n \"More detailed device breakdown: desktop, mobile_app, mobile_web, unknown. Cannot be used alone, must combine with other breakdowns.\",\n category: \"platform\",\n possibleValues: [\"desktop\", \"mobile_app\", \"mobile_web\", \"unknown\"],\n requiresCombination: [\"publisher_platform\"],\n },\n\n // ============================================\n // CREATIVE BREAKDOWNS (Ad Level Only)\n // ============================================\n {\n key: \"image_asset\",\n name: \"Image Asset\",\n description:\n \"Compare different images in dynamic creative ads. Returns asset ID, name, URL, and hash for identification.\",\n category: \"creative\",\n levelRestrictions: [\"ad\"],\n },\n {\n key: \"video_asset\",\n name: \"Video Asset\",\n description:\n \"Compare different videos in dynamic creative. Returns video_id, thumbnail_url for identification.\",\n category: \"creative\",\n levelRestrictions: [\"ad\"],\n },\n {\n key: \"body_asset\",\n name: \"Body Asset (Primary Text)\",\n description:\n \"Compare different ad copy variations. Returns asset ID and full text content.\",\n category: \"creative\",\n levelRestrictions: [\"ad\"],\n },\n {\n key: \"title_asset\",\n name: \"Title Asset (Headline)\",\n description:\n \"Compare headline variations. Returns asset ID and text. Critical for click-through optimization.\",\n category: \"creative\",\n levelRestrictions: [\"ad\"],\n },\n {\n key: \"description_asset\",\n name: \"Description Asset\",\n description:\n \"Compare link description variations that appear below headlines on some placements.\",\n category: \"creative\",\n levelRestrictions: [\"ad\"],\n },\n {\n key: \"call_to_action_asset\",\n name: \"Call to Action Asset\",\n description:\n \"Compare different CTA buttons: Shop Now, Learn More, Sign Up, Download, Contact Us, etc.\",\n category: \"creative\",\n levelRestrictions: [\"ad\"],\n },\n {\n key: \"link_url_asset\",\n name: \"Link URL Asset\",\n description:\n \"Compare results by destination URL. Essential for landing page A/B testing.\",\n category: \"creative\",\n levelRestrictions: [\"ad\"],\n },\n {\n key: \"ad_format_asset\",\n name: \"Ad Format Asset\",\n description:\n \"Compare creative format effectiveness: IMAGE, VIDEO, CAROUSEL, COLLECTION, SLIDESHOW.\",\n category: \"creative\",\n possibleValues: [\"IMAGE\", \"VIDEO\", \"CAROUSEL\", \"COLLECTION\", \"SLIDESHOW\"],\n levelRestrictions: [\"ad\"],\n },\n {\n key: \"media_type\",\n name: \"Media Type\",\n description: \"Broader categorization of creative media: image, video, link, share.\",\n category: \"creative\",\n possibleValues: [\"image\", \"video\", \"link\", \"share\"],\n levelRestrictions: [\"ad\"],\n },\n {\n key: \"media_format\",\n name: \"Media Format\",\n description: \"Specific format of the media creative.\",\n category: \"creative\",\n levelRestrictions: [\"ad\"],\n },\n {\n key: \"flexible_format_asset_type\",\n name: \"Flexible Format Asset Type\",\n description: \"Type of asset in flexible format creative optimization.\",\n category: \"creative\",\n levelRestrictions: [\"ad\"],\n },\n {\n key: \"gen_ai_asset_type\",\n name: \"AI Generated Asset Type\",\n description:\n \"Identifies assets created by Meta's AI tools. Compare AI vs human-created content performance.\",\n category: \"creative\",\n possibleValues: [\"ai_generated_image\", \"ai_generated_text\"],\n levelRestrictions: [\"ad\"],\n },\n\n // ============================================\n // PRODUCT BREAKDOWNS (Catalog/DPA)\n // ============================================\n {\n key: \"product_id\",\n name: \"Product ID\",\n description:\n \"Performance by specific product ID from your catalog. Essential for dynamic product ads optimization.\",\n category: \"product\",\n blocksOffsiteMetrics: true,\n },\n {\n key: \"product_brand_breakdown\",\n name: \"Product Brand\",\n description:\n \"Aggregate performance by brand in your product catalog. Useful for multi-brand retailers.\",\n category: \"product\",\n },\n {\n key: \"product_category_breakdown\",\n name: \"Product Category\",\n description:\n \"Results by product category (electronics, clothing, etc.). Identify best-performing product types.\",\n category: \"product\",\n },\n {\n key: \"product_type_breakdown\",\n name: \"Product Type\",\n description:\n \"More granular than category. Custom product typing from your catalog.\",\n category: \"product\",\n },\n {\n key: \"product_custom_label_0_breakdown\",\n name: \"Product Custom Label 0\",\n description:\n \"Your custom catalog labels. Use for margin tiers, seasonality, promo eligibility, or any business-specific grouping.\",\n category: \"product\",\n },\n {\n key: \"product_custom_label_1_breakdown\",\n name: \"Product Custom Label 1\",\n description: \"Custom label 1 from your product catalog.\",\n category: \"product\",\n },\n {\n key: \"product_custom_label_2_breakdown\",\n name: \"Product Custom Label 2\",\n description: \"Custom label 2 from your product catalog.\",\n category: \"product\",\n },\n {\n key: \"product_custom_label_3_breakdown\",\n name: \"Product Custom Label 3\",\n description: \"Custom label 3 from your product catalog.\",\n category: \"product\",\n },\n {\n key: \"product_custom_label_4_breakdown\",\n name: \"Product Custom Label 4\",\n description: \"Custom label 4 from your product catalog.\",\n category: \"product\",\n },\n {\n key: \"product_group_content_id_breakdown\",\n name: \"Product Group ID\",\n description:\n \"Performance by product group ID. Useful for analyzing variant performance (sizes, colors).\",\n category: \"product\",\n },\n\n // ============================================\n // TIME BREAKDOWNS\n // ============================================\n {\n key: \"hourly_stats_aggregated_by_advertiser_time_zone\",\n name: \"Hourly (Advertiser Timezone)\",\n description:\n \"Hour-of-day performance in your account's timezone. Identify peak performance times for ad scheduling.\",\n category: \"time\",\n historicalLimit: 13,\n blocksOffsiteMetrics: true,\n },\n {\n key: \"hourly_stats_aggregated_by_audience_time_zone\",\n name: \"Hourly (Audience Timezone)\",\n description:\n \"Hour-of-day performance in your audience's local timezone. Better for understanding actual user behavior patterns.\",\n category: \"time\",\n historicalLimit: 13,\n blocksOffsiteMetrics: true,\n },\n {\n key: \"frequency_value\",\n name: \"Frequency Value\",\n description:\n \"Performance by exact impression count (1, 2, 3... times seen). Analyze frequency saturation and optimal exposure levels.\",\n category: \"time\",\n historicalLimit: 6,\n },\n\n // ============================================\n // ACTION BREAKDOWNS\n // ============================================\n {\n key: \"action_type\",\n name: \"Action Type\",\n description:\n \"Default action breakdown. Segments actions/conversions by their type (purchase, lead, add_to_cart, etc.).\",\n category: \"action\",\n },\n {\n key: \"action_device\",\n name: \"Action Device\",\n description:\n \"Device where the conversion occurred: desktop, iphone, ipad, android_smartphone, android_tablet, other.\",\n category: \"action\",\n possibleValues: [\n \"desktop\",\n \"iphone\",\n \"ipad\",\n \"android_smartphone\",\n \"android_tablet\",\n \"other\",\n ],\n blocksOffsiteMetrics: true,\n },\n {\n key: \"action_destination\",\n name: \"Action Destination\",\n description:\n \"Where the action happened: website, app, messenger, instagram_direct, whatsapp, facebook, applinks_deeplink.\",\n category: \"action\",\n possibleValues: [\n \"website\",\n \"app\",\n \"messenger\",\n \"instagram_direct\",\n \"whatsapp\",\n \"facebook\",\n \"applinks_deeplink\",\n ],\n blocksOffsiteMetrics: true,\n },\n {\n key: \"action_target_id\",\n name: \"Action Target ID\",\n description:\n \"The ID of the target (page, app, etc.) where the action occurred.\",\n category: \"action\",\n blocksOffsiteMetrics: true,\n },\n {\n key: \"action_reaction\",\n name: \"Action Reaction\",\n description:\n \"For post reactions: like, love, wow, haha, sorry, anger, pride. Understand emotional response.\",\n category: \"action\",\n possibleValues: [\"like\", \"love\", \"wow\", \"haha\", \"sorry\", \"anger\", \"pride\"],\n },\n {\n key: \"action_video_sound\",\n name: \"Action Video Sound\",\n description:\n \"Whether sound was on or off during video views: on, off, unknown. Indicates engagement depth.\",\n category: \"action\",\n possibleValues: [\"on\", \"off\", \"unknown\"],\n },\n {\n key: \"action_video_type\",\n name: \"Action Video Type\",\n description:\n \"How the video was played: total, auto_play, click_to_play. Understand intentional vs passive viewing.\",\n category: \"action\",\n possibleValues: [\"total\", \"auto_play\", \"click_to_play\"],\n },\n {\n key: \"action_carousel_card_id\",\n name: \"Action Carousel Card ID\",\n description:\n \"Which carousel card drove the action. Optimize card order and content.\",\n category: \"action\",\n blocksOffsiteMetrics: true,\n },\n {\n key: \"action_carousel_card_name\",\n name: \"Action Carousel Card Name\",\n description: \"Name of the carousel card for easier identification.\",\n category: \"action\",\n blocksOffsiteMetrics: true,\n },\n {\n key: \"action_canvas_component_name\",\n name: \"Action Canvas Component Name\",\n description:\n \"Which component in your Instant Experience drove the action.\",\n category: \"action\",\n blocksOffsiteMetrics: true,\n },\n {\n key: \"conversion_destination\",\n name: \"Conversion Destination\",\n description:\n \"Where the conversion occurred: website, app, messaging, calls, on_facebook, on_instagram, offline.\",\n category: \"action\",\n possibleValues: [\n \"website\",\n \"app\",\n \"messaging\",\n \"calls\",\n \"on_facebook\",\n \"on_instagram\",\n \"offline\",\n ],\n },\n {\n key: \"standard_event_content_type\",\n name: \"Standard Event Content Type\",\n description:\n \"Type of content involved in standard events: product, product_group, flight, hotel, etc.\",\n category: \"action\",\n possibleValues: [\"product\", \"product_group\", \"flight\", \"hotel\"],\n },\n {\n key: \"signal_source_bucket\",\n name: \"Signal Source Bucket\",\n description:\n \"Source of conversion data: unmatched, browser_or_server, browser, server. Understand data quality.\",\n category: \"action\",\n possibleValues: [\"unmatched\", \"browser_or_server\", \"browser\", \"server\"],\n },\n {\n key: \"is_business_ai_assisted\",\n name: \"Business AI Assisted\",\n description:\n \"Whether the action was assisted by Meta's business AI features. true/false.\",\n category: \"action\",\n possibleValues: [\"true\", \"false\"],\n },\n\n // ============================================\n // SKADNETWORK BREAKDOWNS (iOS)\n // ============================================\n {\n key: \"skan_campaign_id\",\n name: \"SKAN Campaign ID\",\n description:\n \"Apple's SKAdNetwork campaign identifier for iOS 14.5+ attribution. Values 0-99.\",\n category: \"skan\",\n },\n {\n key: \"skan_conversion_id\",\n name: \"SKAN Conversion ID\",\n description:\n \"The conversion value (0-63) reported by SKAdNetwork. Represents conversion quality tiers.\",\n category: \"skan\",\n },\n {\n key: \"skan_version\",\n name: \"SKAN Version\",\n description:\n \"Which SKAdNetwork version reported the data (2.0, 3.0, 4.0). Affects available data.\",\n category: \"skan\",\n possibleValues: [\"2.0\", \"3.0\", \"4.0\"],\n },\n {\n key: \"coarse_conversion_value\",\n name: \"Coarse Conversion Value\",\n description:\n \"SKAN 4.0+ simplified values: low, medium, high. Broadest conversion quality indicator.\",\n category: \"skan\",\n possibleValues: [\"low\", \"medium\", \"high\"],\n },\n {\n key: \"postback_sequence_index\",\n name: \"Postback Sequence Index\",\n description:\n \"0, 1, or 2 indicating which postback window: 0 (day 0-2), 1 (day 3-7), 2 (day 8-35).\",\n category: \"skan\",\n possibleValues: [\"0\", \"1\", \"2\"],\n },\n {\n key: \"fidelity_type\",\n name: \"Fidelity Type\",\n description:\n \"SKAdNetwork fidelity indicator - confidence level of attribution.\",\n category: \"skan\",\n },\n {\n key: \"redownload\",\n name: \"Redownload\",\n description:\n \"Whether the install was a redownload (returning user) vs new install.\",\n category: \"skan\",\n possibleValues: [\"true\", \"false\"],\n },\n];\n\n// ============================================\n// HELPER FUNCTIONS\n// ============================================\n\n/**\n * Get a breakdown definition by key\n */\nexport function getBreakdownByKey(key: string): BreakdownDefinition | undefined {\n return BREAKDOWN_CATALOG.find((b) => b.key === key);\n}\n\n/**\n * Get breakdowns by category\n */\nexport function getBreakdownsByCategory(\n category: BreakdownCategory\n): BreakdownDefinition[] {\n return BREAKDOWN_CATALOG.filter((b) => b.category === category);\n}\n\n/**\n * Get standard breakdowns (not action breakdowns)\n */\nexport function getStandardBreakdowns(): BreakdownDefinition[] {\n return BREAKDOWN_CATALOG.filter(\n (b) => b.category !== \"action\" && b.category !== \"skan\"\n );\n}\n\n/**\n * Get action breakdowns only\n */\nexport function getActionBreakdowns(): BreakdownDefinition[] {\n return BREAKDOWN_CATALOG.filter((b) => b.category === \"action\");\n}\n\n/**\n * Get creative breakdowns (ad level only)\n */\nexport function getCreativeBreakdowns(): BreakdownDefinition[] {\n return BREAKDOWN_CATALOG.filter((b) => b.category === \"creative\");\n}\n\n/**\n * Check if breakdown is available at a specific level\n */\nexport function isBreakdownAvailableAtLevel(\n breakdownKey: string,\n level: \"ad\" | \"adset\" | \"campaign\" | \"account\"\n): boolean {\n const breakdown = getBreakdownByKey(breakdownKey);\n if (!breakdown) return false;\n\n // If no level restrictions, available at all levels\n if (!breakdown.levelRestrictions) return true;\n\n // Check if the level is in the allowed list\n return breakdown.levelRestrictions.includes(level);\n}\n\n/**\n * Check if breakdown blocks offsite (conversion) metrics\n */\nexport function breakdownBlocksOffsiteMetrics(breakdownKey: string): boolean {\n const breakdown = getBreakdownByKey(breakdownKey);\n return breakdown?.blocksOffsiteMetrics === true;\n}\n\n/**\n * Get required combination breakdowns for a breakdown\n */\nexport function getRequiredCombinations(breakdownKey: string): string[] {\n const breakdown = getBreakdownByKey(breakdownKey);\n return breakdown?.requiresCombination || [];\n}\n\n/**\n * Get historical data limit in months for a breakdown\n */\nexport function getHistoricalLimit(breakdownKey: string): number | null {\n const breakdown = getBreakdownByKey(breakdownKey);\n return breakdown?.historicalLimit || null;\n}\n\n/**\n * Get all breakdown categories with their breakdowns\n */\nexport function getBreakdownsByCategories(): Record<\n BreakdownCategory,\n BreakdownDefinition[]\n> {\n const categories: BreakdownCategory[] = [\n \"demographic\",\n \"audience\",\n \"geographic\",\n \"platform\",\n \"creative\",\n \"product\",\n \"time\",\n \"action\",\n \"skan\",\n ];\n\n return categories.reduce(\n (acc, category) => {\n acc[category] = getBreakdownsByCategory(category);\n return acc;\n },\n {} as Record<BreakdownCategory, BreakdownDefinition[]>\n );\n}\n\n/**\n * Validate breakdown combination\n * Returns list of errors if combination is invalid\n */\nexport function validateBreakdownCombination(\n breakdownKeys: string[],\n level: \"ad\" | \"adset\" | \"campaign\" | \"account\"\n): string[] {\n const errors: string[] = [];\n\n for (const key of breakdownKeys) {\n const breakdown = getBreakdownByKey(key);\n\n if (!breakdown) {\n errors.push(`Unknown breakdown: ${key}`);\n continue;\n }\n\n // Check level restrictions\n if (!isBreakdownAvailableAtLevel(key, level)) {\n errors.push(\n `Breakdown \"${breakdown.name}\" is only available at ${breakdown.levelRestrictions?.join(\", \")} level(s)`\n );\n }\n\n // Check required combinations\n const required = getRequiredCombinations(key);\n for (const reqKey of required) {\n if (!breakdownKeys.includes(reqKey)) {\n errors.push(\n `Breakdown \"${breakdown.name}\" requires \"${reqKey}\" to be included`\n );\n }\n }\n }\n\n // Check for mixing standard and action breakdowns inappropriately\n const hasStandard = breakdownKeys.some(\n (k) =>\n getBreakdownByKey(k)?.category !== \"action\" &&\n getBreakdownByKey(k)?.category !== \"skan\"\n );\n const hasAction = breakdownKeys.some(\n (k) => getBreakdownByKey(k)?.category === \"action\"\n );\n\n if (hasStandard && hasAction) {\n // Some combinations are allowed, but most aren't\n // This is a simplified check - the actual rules are more complex\n const standardKeys = breakdownKeys.filter(\n (k) =>\n getBreakdownByKey(k)?.category !== \"action\" &&\n getBreakdownByKey(k)?.category !== \"skan\"\n );\n const actionKeys = breakdownKeys.filter(\n (k) => getBreakdownByKey(k)?.category === \"action\"\n );\n\n // Only action_type can be combined with some standard breakdowns\n const nonActionTypeActions = actionKeys.filter((k) => k !== \"action_type\");\n if (nonActionTypeActions.length > 0 && standardKeys.length > 0) {\n errors.push(\n `Cannot combine ${nonActionTypeActions.join(\", \")} with standard breakdowns`\n );\n }\n }\n\n return errors;\n}\n\n// ============================================\n// ALLOWED BREAKDOWN COMBINATIONS\n// ============================================\n\n/**\n * Valid breakdown combinations as per Meta API documentation\n */\nexport const VALID_BREAKDOWN_COMBINATIONS: string[][] = [\n // Single breakdowns (always valid)\n [\"age\"],\n [\"gender\"],\n [\"country\"],\n [\"region\"],\n [\"dma\"],\n [\"publisher_platform\"],\n [\"device_platform\"],\n [\"product_id\"],\n [\"hourly_stats_aggregated_by_advertiser_time_zone\"],\n [\"hourly_stats_aggregated_by_audience_time_zone\"],\n [\"frequency_value\"],\n\n // Demographic combinations\n [\"age\", \"gender\"],\n\n // Platform/Device combinations\n [\"action_device\", \"impression_device\"],\n [\"action_device\", \"publisher_platform\"],\n [\"action_device\", \"publisher_platform\", \"impression_device\"],\n [\"action_device\", \"publisher_platform\", \"platform_position\"],\n [\"action_device\", \"publisher_platform\", \"platform_position\", \"impression_device\"],\n [\"publisher_platform\", \"impression_device\"],\n [\"publisher_platform\", \"platform_position\"],\n [\"publisher_platform\", \"platform_position\", \"impression_device\"],\n\n // Reaction combinations\n [\"action_reaction\"],\n [\"action_type\", \"action_reaction\"],\n\n // Carousel combinations\n [\"action_carousel_card_id\"],\n [\"action_carousel_card_name\"],\n [\"action_carousel_card_id\", \"impression_device\"],\n [\"action_carousel_card_id\", \"country\"],\n [\"action_carousel_card_id\", \"age\"],\n [\"action_carousel_card_id\", \"gender\"],\n [\"action_carousel_card_id\", \"age\", \"gender\"],\n];\n\n/**\n * Check if a breakdown combination is explicitly allowed\n */\nexport function isValidBreakdownCombination(breakdownKeys: string[]): boolean {\n // Single breakdown is always valid (if it exists)\n if (breakdownKeys.length === 1) {\n return getBreakdownByKey(breakdownKeys[0]) !== undefined;\n }\n\n // Check against valid combinations\n const sortedKeys = [...breakdownKeys].sort();\n\n return VALID_BREAKDOWN_COMBINATIONS.some((combo) => {\n const sortedCombo = [...combo].sort();\n if (sortedCombo.length !== sortedKeys.length) return false;\n return sortedCombo.every((key, index) => key === sortedKeys[index]);\n });\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// META MARKETING API COMPATIBILITY RULES\n// Defines metric/breakdown compatibility matrix\n// ============================================\n\nimport { InsightsLevel, ValidationResult } from \"./types.js\";\nimport { getMetricByKey } from \"./metric-catalog.js\";\nimport {\n getBreakdownByKey,\n isBreakdownAvailableAtLevel,\n isValidBreakdownCombination,\n} from \"./breakdown-catalog.js\";\n\n// ============================================\n// FIELDS NOT AVAILABLE WITH ANY BREAKDOWN\n// ============================================\n\n/**\n * These fields cannot be requested when using any breakdown\n */\nexport const FIELDS_INCOMPATIBLE_WITH_ALL_BREAKDOWNS: string[] = [\n \"app_store_clicks\",\n \"newsfeed_avg_position\",\n \"newsfeed_clicks\",\n \"relevance_score\",\n \"newsfeed_impressions\",\n];\n\n// ============================================\n// METRIC-SPECIFIC RESTRICTIONS\n// ============================================\n\n/**\n * Video metrics incompatible with hourly breakdowns\n */\nexport const VIDEO_METRICS_NO_HOURLY: string[] = [\n \"video_play_actions\",\n \"video_10_sec_watched_actions\",\n \"video_15_sec_watched_actions\",\n \"video_30_sec_watched_actions\",\n \"video_p25_watched_actions\",\n \"video_p50_watched_actions\",\n \"video_p75_watched_actions\",\n \"video_p95_watched_actions\",\n \"video_p100_watched_actions\",\n \"video_thruplay_watched_actions\",\n \"video_avg_time_watched_actions\",\n \"video_time_watched_actions\",\n \"video_continuous_2_sec_watched_actions\",\n \"video_play_curve_actions\",\n \"video_play_retention_0_to_15s_actions\",\n \"video_play_retention_20_to_60s_actions\",\n \"video_play_retention_graph_actions\",\n];\n\n/**\n * Video metrics incompatible with region breakdown\n */\nexport const VIDEO_METRICS_NO_REGION: string[] = [\n \"video_avg_time_watched_actions\",\n \"video_p25_watched_actions\",\n \"video_p50_watched_actions\",\n \"video_p75_watched_actions\",\n \"video_p95_watched_actions\",\n \"video_p100_watched_actions\",\n];\n\n/**\n * Metrics incompatible with DMA breakdown\n */\nexport const METRICS_NO_DMA: string[] = [\n \"estimated_ad_recall_rate\",\n \"video_thruplay_watched_actions\",\n];\n\n/**\n * Unique-count fields that return 0 with hourly breakdowns\n */\nexport const UNIQUE_FIELDS_RETURN_ZERO_WITH_HOURLY: string[] = [\n \"reach\",\n \"frequency\",\n \"unique_clicks\",\n \"unique_ctr\",\n \"unique_inline_link_clicks\",\n \"unique_inline_link_click_ctr\",\n \"unique_outbound_clicks\",\n \"unique_outbound_clicks_ctr\",\n \"unique_social_clicks\",\n \"cost_per_unique_click\",\n \"cost_per_unique_inline_link_click\",\n \"cost_per_unique_outbound_click\",\n];\n\n/**\n * Hourly breakdown keys\n */\nexport const HOURLY_BREAKDOWNS: string[] = [\n \"hourly_stats_aggregated_by_advertiser_time_zone\",\n \"hourly_stats_aggregated_by_audience_time_zone\",\n];\n\n// ============================================\n// CREATIVE BREAKDOWN RESTRICTIONS\n// ============================================\n\n/**\n * Limited metrics supported with creative breakdowns\n */\nexport const CREATIVE_BREAKDOWN_SUPPORTED_METRICS: string[] = [\n \"impressions\",\n \"clicks\",\n \"spend\",\n \"reach\",\n \"actions\",\n \"action_values\",\n];\n\n/**\n * Creative breakdown keys\n */\nexport const CREATIVE_BREAKDOWNS: string[] = [\n \"image_asset\",\n \"video_asset\",\n \"body_asset\",\n \"title_asset\",\n \"description_asset\",\n \"call_to_action_asset\",\n \"link_url_asset\",\n \"ad_format_asset\",\n \"media_type\",\n \"media_format\",\n \"flexible_format_asset_type\",\n \"gen_ai_asset_type\",\n];\n\n// ============================================\n// OFFSITE METRIC RESTRICTIONS\n// ============================================\n\n/**\n * Breakdowns that block offsite (conversion) metrics entirely\n */\nexport const BREAKDOWNS_BLOCKING_OFFSITE_METRICS: string[] = [\n \"region\",\n \"dma\",\n \"hourly_stats_aggregated_by_advertiser_time_zone\",\n \"hourly_stats_aggregated_by_audience_time_zone\",\n];\n\n/**\n * Breakdowns that partially block offsite metrics\n * (web metrics returned without breakdown value, mobile metrics blocked)\n */\nexport const BREAKDOWNS_PARTIAL_OFFSITE_SUPPORT: string[] = [\n \"action_device\",\n \"action_destination\",\n \"action_target_id\",\n \"product_id\",\n \"action_carousel_card_id\",\n \"action_carousel_card_name\",\n \"action_canvas_component_name\",\n];\n\n/**\n * Action-based metrics (require action arrays)\n */\nexport const ACTION_BASED_METRICS: string[] = [\n \"actions\",\n \"action_values\",\n \"conversions\",\n \"conversion_values\",\n \"cost_per_action_type\",\n \"cost_per_conversion\",\n \"purchase_roas\",\n \"website_purchase_roas\",\n \"mobile_app_purchase_roas\",\n \"outbound_clicks\",\n \"unique_outbound_clicks\",\n \"outbound_clicks_ctr\",\n \"unique_outbound_clicks_ctr\",\n \"cost_per_outbound_click\",\n \"cost_per_unique_outbound_click\",\n \"video_play_actions\",\n \"video_10_sec_watched_actions\",\n \"video_15_sec_watched_actions\",\n \"video_30_sec_watched_actions\",\n \"video_p25_watched_actions\",\n \"video_p50_watched_actions\",\n \"video_p75_watched_actions\",\n \"video_p95_watched_actions\",\n \"video_p100_watched_actions\",\n \"video_thruplay_watched_actions\",\n \"video_avg_time_watched_actions\",\n \"cost_per_thruplay\",\n];\n\n// ============================================\n// VALIDATION FUNCTIONS\n// ============================================\n\n/**\n * Check if a metric is compatible with a specific breakdown\n */\nexport function isMetricCompatibleWithBreakdown(\n metricKey: string,\n breakdownKey: string\n): { compatible: boolean; reason?: string } {\n const metric = getMetricByKey(metricKey);\n const breakdown = getBreakdownByKey(breakdownKey);\n\n if (!metric) {\n return { compatible: false, reason: `Unknown metric: ${metricKey}` };\n }\n\n if (!breakdown) {\n return { compatible: false, reason: `Unknown breakdown: ${breakdownKey}` };\n }\n\n // Check if metric is globally incompatible with breakdowns\n if (FIELDS_INCOMPATIBLE_WITH_ALL_BREAKDOWNS.includes(metricKey)) {\n return {\n compatible: false,\n reason: `${metric.name} cannot be used with any breakdown`,\n };\n }\n\n // Check hourly breakdown restrictions for video metrics\n if (HOURLY_BREAKDOWNS.includes(breakdownKey)) {\n if (VIDEO_METRICS_NO_HOURLY.includes(metricKey)) {\n return {\n compatible: false,\n reason: `${metric.name} is incompatible with hourly breakdowns`,\n };\n }\n if (UNIQUE_FIELDS_RETURN_ZERO_WITH_HOURLY.includes(metricKey)) {\n return {\n compatible: true,\n reason: `Warning: ${metric.name} will return 0 with hourly breakdowns`,\n };\n }\n }\n\n // Check region breakdown restrictions\n if (breakdownKey === \"region\") {\n if (VIDEO_METRICS_NO_REGION.includes(metricKey)) {\n return {\n compatible: false,\n reason: `${metric.name} is incompatible with region breakdown`,\n };\n }\n }\n\n // Check DMA breakdown restrictions\n if (breakdownKey === \"dma\") {\n if (METRICS_NO_DMA.includes(metricKey)) {\n return {\n compatible: false,\n reason: `${metric.name} is incompatible with DMA breakdown`,\n };\n }\n }\n\n // Check creative breakdown restrictions\n if (CREATIVE_BREAKDOWNS.includes(breakdownKey)) {\n if (!CREATIVE_BREAKDOWN_SUPPORTED_METRICS.includes(metricKey)) {\n // For calculated metrics, check if their required fields are supported\n if (metric.type === \"calculated\" && metric.requiredFields) {\n const unsupportedFields = metric.requiredFields.filter(\n (f) => !CREATIVE_BREAKDOWN_SUPPORTED_METRICS.includes(f)\n );\n if (unsupportedFields.length > 0) {\n return {\n compatible: false,\n reason: `${metric.name} requires fields not supported with creative breakdowns: ${unsupportedFields.join(\", \")}`,\n };\n }\n } else {\n return {\n compatible: false,\n reason: `${metric.name} is not supported with creative breakdowns. Only impressions, clicks, spend, reach, actions, action_values are supported.`,\n };\n }\n }\n }\n\n // Check if breakdown blocks offsite metrics\n if (BREAKDOWNS_BLOCKING_OFFSITE_METRICS.includes(breakdownKey)) {\n // Check if metric is an offsite/conversion metric\n if (\n metric.category === \"conversion\" ||\n metricKey.includes(\"conversion\") ||\n metricKey.includes(\"purchase\") ||\n metricKey.includes(\"roas\")\n ) {\n return {\n compatible: false,\n reason: `${metric.name} (offsite metric) is not available with ${breakdown.name}`,\n };\n }\n }\n\n // Check metric's own compatibility list\n if (\n metric.incompatibleBreakdowns &&\n metric.incompatibleBreakdowns.includes(breakdownKey)\n ) {\n return {\n compatible: false,\n reason: `${metric.name} is explicitly incompatible with ${breakdown.name}`,\n };\n }\n\n return { compatible: true };\n}\n\n/**\n * Validate a complete query selection\n */\nexport function validateQuerySelection(\n metrics: string[],\n breakdowns: string[],\n actionBreakdowns: string[],\n level: InsightsLevel\n): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check each metric against each breakdown\n for (const metricKey of metrics) {\n const metric = getMetricByKey(metricKey);\n\n // Check for deprecated metrics\n if (metric?.isDeprecated) {\n warnings.push(\n `\"${metric.name}\" is deprecated and no longer supported by Meta API. It will be skipped.`\n );\n continue; // Skip further checks for deprecated metrics\n }\n\n // Check against standard breakdowns\n for (const breakdownKey of breakdowns) {\n const result = isMetricCompatibleWithBreakdown(metricKey, breakdownKey);\n if (!result.compatible) {\n errors.push(result.reason!);\n } else if (result.reason) {\n warnings.push(result.reason);\n }\n }\n\n // Check level restrictions for metrics\n if (metric?.levelRestrictions && !metric.levelRestrictions.includes(level)) {\n errors.push(\n `${metric.name} is only available at ${metric.levelRestrictions.join(\", \")} level(s)`\n );\n }\n }\n\n // Check breakdown level restrictions\n for (const breakdownKey of breakdowns) {\n if (!isBreakdownAvailableAtLevel(breakdownKey, level)) {\n const breakdown = getBreakdownByKey(breakdownKey);\n errors.push(\n `${breakdown?.name || breakdownKey} is only available at ad level`\n );\n }\n }\n\n // Check if mixing standard breakdowns with action breakdowns inappropriately\n if (breakdowns.length > 0 && actionBreakdowns.length > 0) {\n // Action breakdowns (like action_type) can be used alongside standard breakdowns\n // BUT not with creative breakdowns\n const hasCreativeBreakdown = breakdowns.some((b) =>\n CREATIVE_BREAKDOWNS.includes(b)\n );\n if (hasCreativeBreakdown) {\n errors.push(\n \"Cannot combine action_breakdowns with creative breakdowns. Use separate API requests.\"\n );\n }\n }\n\n // Check breakdown combination validity\n if (breakdowns.length > 1) {\n if (!isValidBreakdownCombination(breakdowns)) {\n warnings.push(\n `The breakdown combination [${breakdowns.join(\", \")}] may not be officially supported. Results may vary.`\n );\n }\n }\n\n // Check historical data limits\n const breakdownsWithLimits = breakdowns.filter((b) => {\n const bd = getBreakdownByKey(b);\n return bd?.historicalLimit !== undefined;\n });\n if (breakdownsWithLimits.length > 0) {\n const limits = breakdownsWithLimits.map((b) => {\n const bd = getBreakdownByKey(b);\n return `${bd?.name}: ${bd?.historicalLimit} months`;\n });\n warnings.push(\n `Historical data limits apply: ${limits.join(\", \")}. Queries beyond these limits may return incomplete data.`\n );\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Determine if metrics need to be split into separate API calls\n */\nexport function needsSeparateRequests(\n metrics: string[],\n breakdowns: string[],\n _actionBreakdowns: string[]\n): { needsSplit: boolean; reason?: string } {\n // Check if we have both action-based metrics and creative breakdowns\n const hasActionMetrics = metrics.some((m) => ACTION_BASED_METRICS.includes(m));\n const hasCreativeBreakdowns = breakdowns.some((b) =>\n CREATIVE_BREAKDOWNS.includes(b)\n );\n\n if (hasActionMetrics && hasCreativeBreakdowns) {\n return {\n needsSplit: true,\n reason:\n \"Action-based metrics cannot be combined with creative breakdowns in a single request\",\n };\n }\n\n // Check if we have calculated metrics that need different base metrics\n const hasConversionMetrics = metrics.some((m) => {\n const metric = getMetricByKey(m);\n return metric?.category === \"conversion\";\n });\n\n // If we have breakdowns that block offsite metrics but we need conversions\n if (hasConversionMetrics) {\n const blockingBreakdowns = breakdowns.filter((b) =>\n BREAKDOWNS_BLOCKING_OFFSITE_METRICS.includes(b)\n );\n if (blockingBreakdowns.length > 0) {\n return {\n needsSplit: true,\n reason: `Conversion metrics cannot be retrieved with ${blockingBreakdowns.join(\", \")} breakdown(s). Need separate requests.`,\n };\n }\n }\n\n return { needsSplit: false };\n}\n\n/**\n * Get list of metrics that must be fetched separately\n * Returns groups of metrics that can be fetched together\n */\nexport function groupMetricsByCompatibility(\n metrics: string[],\n breakdowns: string[]\n): { group: string[]; breakdownsAllowed: string[]; reason?: string }[] {\n const groups: {\n group: string[];\n breakdownsAllowed: string[];\n reason?: string;\n }[] = [];\n\n // Separate action-based metrics from delivery metrics when creative breakdowns are used\n const hasCreativeBreakdowns = breakdowns.some((b) =>\n CREATIVE_BREAKDOWNS.includes(b)\n );\n\n if (hasCreativeBreakdowns) {\n const supportedMetrics = metrics.filter((m) =>\n CREATIVE_BREAKDOWN_SUPPORTED_METRICS.includes(m)\n );\n const unsupportedMetrics = metrics.filter(\n (m) => !CREATIVE_BREAKDOWN_SUPPORTED_METRICS.includes(m)\n );\n\n if (supportedMetrics.length > 0) {\n groups.push({\n group: supportedMetrics,\n breakdownsAllowed: breakdowns,\n reason: \"Metrics compatible with creative breakdowns\",\n });\n }\n\n if (unsupportedMetrics.length > 0) {\n // These need to be fetched without creative breakdowns\n const nonCreativeBreakdowns = breakdowns.filter(\n (b) => !CREATIVE_BREAKDOWNS.includes(b)\n );\n groups.push({\n group: unsupportedMetrics,\n breakdownsAllowed: nonCreativeBreakdowns,\n reason: \"Metrics incompatible with creative breakdowns - fetched separately\",\n });\n }\n } else {\n // No creative breakdowns - check for other incompatibilities\n const compatibleWithAll: string[] = [];\n const needsSeparate: { metric: string; incompatibleWith: string[] }[] = [];\n\n for (const metric of metrics) {\n const incompatibleBreakdowns: string[] = [];\n\n for (const breakdown of breakdowns) {\n const result = isMetricCompatibleWithBreakdown(metric, breakdown);\n if (!result.compatible) {\n incompatibleBreakdowns.push(breakdown);\n }\n }\n\n if (incompatibleBreakdowns.length === 0) {\n compatibleWithAll.push(metric);\n } else {\n needsSeparate.push({ metric, incompatibleWith: incompatibleBreakdowns });\n }\n }\n\n if (compatibleWithAll.length > 0) {\n groups.push({\n group: compatibleWithAll,\n breakdownsAllowed: breakdowns,\n reason: \"Fully compatible metrics\",\n });\n }\n\n // Group metrics by their incompatibilities\n for (const item of needsSeparate) {\n const allowedBreakdowns = breakdowns.filter(\n (b) => !item.incompatibleWith.includes(b)\n );\n groups.push({\n group: [item.metric],\n breakdownsAllowed: allowedBreakdowns,\n reason: `Incompatible with: ${item.incompatibleWith.join(\", \")}`,\n });\n }\n }\n\n return groups;\n}\n\n/**\n * Get the API field names for a list of metrics\n * Some metrics have different API field names than their keys\n *\n * IMPORTANT: This function recursively resolves metric keys to API fields.\n * Calculated metrics have `requiredFields` which may contain other metric keys\n * that need to be resolved to their API field names.\n */\nexport function getApiFieldsForMetrics(metricKeys: string[]): string[] {\n const fields: string[] = [];\n\n // Raw API field names that should be passed through directly\n // These are valid Meta API field names, not metric keys\n const RAW_API_FIELDS = [\n \"actions\",\n \"action_values\",\n \"cost_per_action_type\",\n \"conversions\",\n \"conversion_values\",\n // Video metrics API fields\n \"video_play_actions\",\n \"video_p25_watched_actions\",\n \"video_p50_watched_actions\",\n \"video_p75_watched_actions\",\n \"video_p95_watched_actions\",\n \"video_p100_watched_actions\",\n \"video_thruplay_watched_actions\",\n \"video_time_watched_actions\",\n \"video_avg_time_watched_actions\",\n \"video_30_sec_watched_actions\",\n \"video_15_sec_watched_actions\",\n ];\n\n // Helper function to resolve a single metric key to API field(s)\n // Uses a Set to track visited keys and prevent infinite recursion\n const resolveMetricKey = (key: string, visited: Set<string>): string[] => {\n // Prevent infinite recursion\n if (visited.has(key)) return [];\n visited.add(key);\n\n // If it's a raw API field name, return it directly\n if (RAW_API_FIELDS.includes(key)) {\n return [key];\n }\n\n const metric = getMetricByKey(key);\n if (!metric) {\n // Unknown key - might be a raw API field we didn't list, return as-is\n return [key];\n }\n\n if (metric.type === \"calculated\" && metric.requiredFields) {\n // For calculated metrics, recursively resolve their required fields\n // This handles cases where requiredFields contains metric keys (not API fields)\n const resolvedFields: string[] = [];\n for (const reqField of metric.requiredFields) {\n resolvedFields.push(...resolveMetricKey(reqField, visited));\n }\n return resolvedFields;\n } else {\n // For native metrics, use apiField if specified, otherwise use key\n return [metric.apiField || key];\n }\n };\n\n // Process all metric keys\n for (const key of metricKeys) {\n fields.push(...resolveMetricKey(key, new Set()));\n }\n\n // Remove duplicates\n return [...new Set(fields)];\n}\n\n/**\n * Check if any metrics require action_breakdowns\n */\nexport function metricsRequireActionBreakdowns(metrics: string[]): boolean {\n return metrics.some((m) => {\n const metric = getMetricByKey(m);\n // Action-based metrics benefit from action_type breakdown\n return (\n ACTION_BASED_METRICS.includes(m) ||\n metric?.actionType !== undefined ||\n metric?.category === \"conversion\"\n );\n });\n}\n\n/**\n * Check if a single metric is action-based (needs actions/action_values fields)\n * This handles both direct action fields and metrics that extract from action arrays\n */\nexport function isActionBasedMetric(metricKey: string): boolean {\n // Direct action-based metrics\n if (ACTION_BASED_METRICS.includes(metricKey)) {\n return true;\n }\n\n // Check if metric extracts from action arrays (has actionType defined)\n const metric = getMetricByKey(metricKey);\n if (!metric) return false;\n\n // Metrics that have actionType are extracted from actions/action_values arrays\n if (metric.actionType !== undefined) {\n return true;\n }\n\n // Metrics that use actions, action_values, or cost_per_action_type as their API field\n if (\n metric.apiField === \"actions\" ||\n metric.apiField === \"action_values\" ||\n metric.apiField === \"cost_per_action_type\"\n ) {\n return true;\n }\n\n return false;\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// DIMENSION CATALOG\n// COMPLETE Entity identifiers and metadata fields\n// Based on Meta Marketing API v26.0\n// ============================================\n\n/**\n * Dimensions are entity identifiers and metadata fields that provide context about your ads.\n * Unlike metrics (numeric values) or breakdowns (segmentation), dimensions identify\n * WHICH entity (campaign, ad set, ad, creative) the data belongs to.\n * They are essential for data joins, display, and drill-down navigation.\n *\n * This catalog includes ALL available dimensions from Meta API:\n * - Entity IDs and names\n * - Statuses (effective, configured)\n * - Budget information\n * - Timestamps (created, updated, start, end)\n * - Configuration settings\n * - Creative details\n */\n\nexport type DimensionCategory =\n | \"account\"\n | \"campaign\"\n | \"adset\"\n | \"ad\"\n | \"creative\"\n | \"time\"\n | \"label\"\n | \"attribution\";\n\nexport type DimensionType = \"string\" | \"enum\" | \"array\" | \"number\" | \"boolean\" | \"object\";\n\nexport type InsightsLevel = \"account\" | \"campaign\" | \"adset\" | \"ad\";\n\nexport interface DimensionDefinition {\n key: string; // Unique identifier (e.g., \"campaign_id\")\n apiField: string; // Meta API field name (same as key for most)\n name: string; // Display name (e.g., \"Campaign ID\")\n description: string; // Full description for tooltips\n category: DimensionCategory;\n type: DimensionType;\n level: InsightsLevel[]; // Which levels return this dimension\n isIdentifier: boolean; // True for ID fields used for joins\n enumValues?: string[]; // Possible values for enum types\n isStatus?: boolean; // True for status fields\n isBudget?: boolean; // True for budget fields\n isTimestamp?: boolean; // True for time/date fields\n isInsightsField?: boolean; // False if only available via entity API, not insights (default: true)\n}\n\n// ============================================\n// DIMENSION CATALOG - COMPLETE\n// ============================================\n\nexport const DIMENSION_CATALOG: DimensionDefinition[] = [\n // ============================================\n // ACCOUNT DIMENSIONS\n // ============================================\n {\n key: \"account_id\",\n apiField: \"account_id\",\n name: \"Account ID\",\n description: \"Unique numeric ID of the ad account. Format: numeric string without 'act_' prefix. Use for data joins across systems. Always returned in insights response.\",\n category: \"account\",\n type: \"string\",\n level: [\"account\", \"campaign\", \"adset\", \"ad\"],\n isIdentifier: true,\n },\n {\n key: \"account_name\",\n apiField: \"account_name\",\n name: \"Account Name\",\n description: \"The display name configured for the ad account in Business Manager. Useful for multi-account reports.\",\n category: \"account\",\n type: \"string\",\n level: [\"account\", \"campaign\", \"adset\", \"ad\"],\n isIdentifier: false,\n },\n {\n key: \"account_currency\",\n apiField: \"account_currency\",\n name: \"Currency\",\n description: \"Three-letter ISO currency code (EUR, USD, GBP). All monetary values in the response use this currency. Essential for financial calculations.\",\n category: \"account\",\n type: \"string\",\n level: [\"account\", \"campaign\", \"adset\", \"ad\"],\n isIdentifier: false,\n },\n {\n key: \"account_timezone_name\",\n apiField: \"timezone_name\",\n name: \"Timezone\",\n description: \"The timezone configured for the ad account (e.g., 'Europe/Paris', 'America/New_York'). Affects how dates are reported.\",\n category: \"account\",\n type: \"string\",\n level: [\"account\"],\n isIdentifier: false,\n },\n {\n key: \"business_name\",\n apiField: \"business.name\",\n name: \"Business Name\",\n description: \"Name of the Business Manager that owns this ad account. Useful for enterprise multi-business reporting.\",\n category: \"account\",\n type: \"string\",\n level: [\"account\"],\n isIdentifier: false,\n },\n {\n key: \"business_id\",\n apiField: \"business.id\",\n name: \"Business ID\",\n description: \"Unique ID of the Business Manager that owns this ad account.\",\n category: \"account\",\n type: \"string\",\n level: [\"account\"],\n isIdentifier: true,\n },\n\n // ============================================\n // CAMPAIGN DIMENSIONS\n // ============================================\n {\n key: \"campaign_id\",\n apiField: \"campaign_id\",\n name: \"Campaign ID\",\n description: \"Unique numeric ID for the campaign. Primary key for campaign-level data. Use for joins and drill-down navigation.\",\n category: \"campaign\",\n type: \"string\",\n level: [\"campaign\", \"adset\", \"ad\"],\n isIdentifier: true,\n },\n {\n key: \"campaign_name\",\n apiField: \"campaign_name\",\n name: \"Campaign Name\",\n description: \"The display name you set for the campaign in Ads Manager. Displayed to users in reports and tables.\",\n category: \"campaign\",\n type: \"string\",\n level: [\"campaign\", \"adset\", \"ad\"],\n isIdentifier: false,\n },\n {\n key: \"objective\",\n apiField: \"objective\",\n name: \"Objective\",\n description: \"Campaign optimization goal determining what Meta optimizes for. Values: OUTCOME_AWARENESS, OUTCOME_ENGAGEMENT, OUTCOME_LEADS, OUTCOME_SALES, OUTCOME_APP_PROMOTION, OUTCOME_TRAFFIC.\",\n category: \"campaign\",\n type: \"enum\",\n level: [\"campaign\", \"adset\", \"ad\"],\n isIdentifier: false,\n enumValues: [\n \"OUTCOME_AWARENESS\",\n \"OUTCOME_ENGAGEMENT\",\n \"OUTCOME_LEADS\",\n \"OUTCOME_SALES\",\n \"OUTCOME_APP_PROMOTION\",\n \"OUTCOME_TRAFFIC\"\n ],\n },\n {\n key: \"buying_type\",\n apiField: \"buying_type\",\n name: \"Buying Type\",\n description: \"Purchase method: AUCTION (standard bidding) or RESERVED (guaranteed delivery). Most campaigns use AUCTION.\",\n category: \"campaign\",\n type: \"enum\",\n level: [\"campaign\", \"adset\", \"ad\"],\n isIdentifier: false,\n enumValues: [\"AUCTION\", \"RESERVED\"],\n },\n {\n key: \"campaign_effective_status\",\n apiField: \"effective_status\",\n name: \"Campaign Status (Effective)\",\n description: \"Computed campaign status considering parent and system states. Values: ACTIVE, PAUSED, DELETED, ARCHIVED, IN_PROCESS, WITH_ISSUES. This reflects actual delivery state.\",\n category: \"campaign\",\n type: \"enum\",\n level: [\"campaign\"],\n isIdentifier: false,\n isStatus: true,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n enumValues: [\"ACTIVE\", \"PAUSED\", \"DELETED\", \"ARCHIVED\", \"IN_PROCESS\", \"WITH_ISSUES\", \"CAMPAIGN_PAUSED\", \"ADSET_PAUSED\"],\n },\n {\n key: \"campaign_status\",\n apiField: \"status\",\n name: \"Campaign Status (Configured)\",\n description: \"The status you configured for the campaign: ACTIVE, PAUSED, DELETED, ARCHIVED. Does not reflect system-level pausing.\",\n category: \"campaign\",\n type: \"enum\",\n level: [\"campaign\"],\n isIdentifier: false,\n isStatus: true,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n enumValues: [\"ACTIVE\", \"PAUSED\", \"DELETED\", \"ARCHIVED\"],\n },\n {\n key: \"campaign_daily_budget\",\n apiField: \"daily_budget\",\n name: \"Campaign Daily Budget\",\n description: \"Daily spending limit for the campaign in account currency (in cents). Divide by 100 for display value.\",\n category: \"campaign\",\n type: \"number\",\n level: [\"campaign\"],\n isIdentifier: false,\n isBudget: true,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n },\n {\n key: \"campaign_lifetime_budget\",\n apiField: \"lifetime_budget\",\n name: \"Campaign Lifetime Budget\",\n description: \"Total budget for the campaign lifetime in account currency (in cents). Used for campaign budget optimization.\",\n category: \"campaign\",\n type: \"number\",\n level: [\"campaign\"],\n isIdentifier: false,\n isBudget: true,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n },\n {\n key: \"campaign_budget_remaining\",\n apiField: \"budget_remaining\",\n name: \"Campaign Budget Remaining\",\n description: \"Remaining budget to spend (lifetime budgets only). In cents.\",\n category: \"campaign\",\n type: \"number\",\n level: [\"campaign\"],\n isIdentifier: false,\n isBudget: true,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n },\n {\n key: \"campaign_spend_cap\",\n apiField: \"spend_cap\",\n name: \"Campaign Spend Cap\",\n description: \"Maximum amount the campaign can spend in total. In cents.\",\n category: \"campaign\",\n type: \"number\",\n level: [\"campaign\"],\n isIdentifier: false,\n isBudget: true,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n },\n {\n key: \"campaign_start_time\",\n apiField: \"start_time\",\n name: \"Campaign Start Time\",\n description: \"Scheduled start time for the campaign in ISO 8601 format.\",\n category: \"campaign\",\n type: \"string\",\n level: [\"campaign\"],\n isIdentifier: false,\n isTimestamp: true,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n },\n {\n key: \"campaign_stop_time\",\n apiField: \"stop_time\",\n name: \"Campaign End Time\",\n description: \"Scheduled end time for the campaign in ISO 8601 format. Null if no end date set.\",\n category: \"campaign\",\n type: \"string\",\n level: [\"campaign\"],\n isIdentifier: false,\n isTimestamp: true,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n },\n {\n key: \"campaign_created_time\",\n apiField: \"created_time\",\n name: \"Campaign Created\",\n description: \"When the campaign was created in ISO 8601 format.\",\n category: \"campaign\",\n type: \"string\",\n level: [\"campaign\"],\n isIdentifier: false,\n isTimestamp: true,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n },\n {\n key: \"campaign_updated_time\",\n apiField: \"updated_time\",\n name: \"Campaign Updated\",\n description: \"When the campaign was last modified in ISO 8601 format.\",\n category: \"campaign\",\n type: \"string\",\n level: [\"campaign\"],\n isIdentifier: false,\n isTimestamp: true,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n },\n {\n key: \"special_ad_categories\",\n apiField: \"special_ad_categories\",\n name: \"Special Ad Categories\",\n description: \"Special categories requiring additional restrictions: HOUSING, EMPLOYMENT, CREDIT, SOCIAL_ISSUES_ELECTIONS_OR_POLITICS. Empty array if none.\",\n category: \"campaign\",\n type: \"array\",\n level: [\"campaign\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n enumValues: [\"HOUSING\", \"EMPLOYMENT\", \"CREDIT\", \"SOCIAL_ISSUES_ELECTIONS_OR_POLITICS\", \"NONE\"],\n },\n {\n key: \"campaign_bid_strategy\",\n apiField: \"bid_strategy\",\n name: \"Campaign Bid Strategy\",\n description: \"Campaign-level bid strategy when using campaign budget optimization (CBO).\",\n category: \"campaign\",\n type: \"enum\",\n level: [\"campaign\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n enumValues: [\"LOWEST_COST_WITHOUT_CAP\", \"LOWEST_COST_WITH_BID_CAP\", \"COST_CAP\", \"LOWEST_COST_WITH_MIN_ROAS\"],\n },\n {\n key: \"source_campaign_id\",\n apiField: \"source_campaign_id\",\n name: \"Source Campaign ID\",\n description: \"ID of the original campaign if this was duplicated. Useful for tracking copies.\",\n category: \"campaign\",\n type: \"string\",\n level: [\"campaign\"],\n isIdentifier: true,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n },\n {\n key: \"smart_promotion_type\",\n apiField: \"smart_promotion_type\",\n name: \"Smart Promotion Type\",\n description: \"Type of smart promotion if enabled: GUIDED_CREATION, SMART_APP_PROMOTION.\",\n category: \"campaign\",\n type: \"enum\",\n level: [\"campaign\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n enumValues: [\"GUIDED_CREATION\", \"SMART_APP_PROMOTION\"],\n },\n\n // ============================================\n // AD SET DIMENSIONS\n // ============================================\n {\n key: \"adset_id\",\n apiField: \"adset_id\",\n name: \"Ad Set ID\",\n description: \"Unique numeric ID for the ad set. Primary key for ad set-level data. Links to parent campaign via campaign_id.\",\n category: \"adset\",\n type: \"string\",\n level: [\"adset\", \"ad\"],\n isIdentifier: true,\n },\n {\n key: \"adset_name\",\n apiField: \"adset_name\",\n name: \"Ad Set Name\",\n description: \"The display name you set for the ad set. Contains targeting and budget context.\",\n category: \"adset\",\n type: \"string\",\n level: [\"adset\", \"ad\"],\n isIdentifier: false,\n },\n {\n key: \"optimization_goal\",\n apiField: \"optimization_goal\",\n name: \"Optimization Goal\",\n description: \"What Meta optimizes ad delivery for at the ad set level. Values: LINK_CLICKS, IMPRESSIONS, CONVERSIONS, REACH, LANDING_PAGE_VIEWS, OFFSITE_CONVERSIONS, VALUE, etc.\",\n category: \"adset\",\n type: \"enum\",\n level: [\"adset\", \"ad\"],\n isIdentifier: false,\n enumValues: [\n \"NONE\",\n \"APP_INSTALLS\",\n \"AD_RECALL_LIFT\",\n \"ENGAGED_USERS\",\n \"EVENT_RESPONSES\",\n \"IMPRESSIONS\",\n \"LEAD_GENERATION\",\n \"QUALITY_LEAD\",\n \"LINK_CLICKS\",\n \"OFFSITE_CONVERSIONS\",\n \"PAGE_LIKES\",\n \"POST_ENGAGEMENT\",\n \"QUALITY_CALL\",\n \"REACH\",\n \"LANDING_PAGE_VIEWS\",\n \"VISIT_INSTAGRAM_PROFILE\",\n \"VALUE\",\n \"THRUPLAY\",\n \"DERIVED_EVENTS\",\n \"APP_INSTALLS_AND_OFFSITE_CONVERSIONS\",\n \"CONVERSATIONS\",\n \"IN_APP_VALUE\",\n \"MESSAGING_PURCHASE_CONVERSION\",\n \"MESSAGING_APPOINTMENT_CONVERSION\",\n \"SUBSCRIBERS\"\n ],\n },\n {\n key: \"bid_strategy\",\n apiField: \"bid_strategy\",\n name: \"Bid Strategy\",\n description: \"How Meta bids in the auction for this ad set. Values: LOWEST_COST_WITHOUT_CAP (auto), LOWEST_COST_WITH_BID_CAP, COST_CAP, MINIMUM_ROAS.\",\n category: \"adset\",\n type: \"enum\",\n level: [\"adset\", \"ad\"],\n isIdentifier: false,\n enumValues: [\n \"LOWEST_COST_WITHOUT_CAP\",\n \"LOWEST_COST_WITH_BID_CAP\",\n \"COST_CAP\",\n \"LOWEST_COST_WITH_MIN_ROAS\"\n ],\n },\n {\n key: \"bid_amount\",\n apiField: \"bid_amount\",\n name: \"Bid Amount\",\n description: \"The bid amount in cents when using bid cap or cost cap.\",\n category: \"adset\",\n type: \"number\",\n level: [\"adset\"],\n isIdentifier: false,\n isBudget: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"budget_remaining\",\n apiField: \"budget_remaining\",\n name: \"Budget Remaining\",\n description: \"Amount of budget left to spend (lifetime budgets only). Null for daily budgets.\",\n category: \"adset\",\n type: \"string\",\n level: [\"adset\", \"ad\"],\n isIdentifier: false,\n isBudget: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"adset_effective_status\",\n apiField: \"effective_status\",\n name: \"Ad Set Status (Effective)\",\n description: \"Computed status considering campaign and system states. Values: ACTIVE, PAUSED, DELETED, ARCHIVED, CAMPAIGN_PAUSED, WITH_ISSUES.\",\n category: \"adset\",\n type: \"enum\",\n level: [\"adset\"],\n isIdentifier: false,\n isStatus: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n enumValues: [\"ACTIVE\", \"PAUSED\", \"DELETED\", \"ARCHIVED\", \"CAMPAIGN_PAUSED\", \"IN_PROCESS\", \"WITH_ISSUES\"],\n },\n {\n key: \"adset_status\",\n apiField: \"status\",\n name: \"Ad Set Status (Configured)\",\n description: \"The status you configured for the ad set: ACTIVE, PAUSED, DELETED, ARCHIVED.\",\n category: \"adset\",\n type: \"enum\",\n level: [\"adset\"],\n isIdentifier: false,\n isStatus: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n enumValues: [\"ACTIVE\", \"PAUSED\", \"DELETED\", \"ARCHIVED\"],\n },\n {\n key: \"adset_daily_budget\",\n apiField: \"daily_budget\",\n name: \"Ad Set Daily Budget\",\n description: \"Daily spending limit for the ad set in cents.\",\n category: \"adset\",\n type: \"number\",\n level: [\"adset\"],\n isIdentifier: false,\n isBudget: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"adset_lifetime_budget\",\n apiField: \"lifetime_budget\",\n name: \"Ad Set Lifetime Budget\",\n description: \"Total budget for the ad set lifetime in cents.\",\n category: \"adset\",\n type: \"number\",\n level: [\"adset\"],\n isIdentifier: false,\n isBudget: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"adset_start_time\",\n apiField: \"start_time\",\n name: \"Ad Set Start Time\",\n description: \"Scheduled start time for the ad set in ISO 8601 format.\",\n category: \"adset\",\n type: \"string\",\n level: [\"adset\"],\n isIdentifier: false,\n isTimestamp: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"adset_end_time\",\n apiField: \"end_time\",\n name: \"Ad Set End Time\",\n description: \"Scheduled end time for the ad set in ISO 8601 format. Null if no end date set.\",\n category: \"adset\",\n type: \"string\",\n level: [\"adset\"],\n isIdentifier: false,\n isTimestamp: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"adset_created_time\",\n apiField: \"created_time\",\n name: \"Ad Set Created\",\n description: \"When the ad set was created in ISO 8601 format.\",\n category: \"adset\",\n type: \"string\",\n level: [\"adset\"],\n isIdentifier: false,\n isTimestamp: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"adset_updated_time\",\n apiField: \"updated_time\",\n name: \"Ad Set Updated\",\n description: \"When the ad set was last modified in ISO 8601 format.\",\n category: \"adset\",\n type: \"string\",\n level: [\"adset\"],\n isIdentifier: false,\n isTimestamp: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"billing_event\",\n apiField: \"billing_event\",\n name: \"Billing Event\",\n description: \"Event that triggers billing: IMPRESSIONS, LINK_CLICKS, APP_INSTALLS, PAGE_LIKES, POST_ENGAGEMENT, VIDEO_VIEWS, THRUPLAY.\",\n category: \"adset\",\n type: \"enum\",\n level: [\"adset\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n enumValues: [\"APP_INSTALLS\", \"IMPRESSIONS\", \"LINK_CLICKS\", \"NONE\", \"OFFER_CLAIMS\", \"PAGE_LIKES\", \"POST_ENGAGEMENT\", \"THRUPLAY\", \"PURCHASE\", \"LISTING_INTERACTION\"],\n },\n {\n key: \"destination_type\",\n apiField: \"destination_type\",\n name: \"Destination Type\",\n description: \"Where users are sent: WEBSITE, APP, MESSENGER, INSTAGRAM_DIRECT, WHATSAPP, APPLINKS_AUTOMATIC, FACEBOOK.\",\n category: \"adset\",\n type: \"enum\",\n level: [\"adset\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n enumValues: [\"UNDEFINED\", \"WEBSITE\", \"APP\", \"MESSENGER\", \"APPLINKS_AUTOMATIC\", \"FACEBOOK\", \"INSTAGRAM_DIRECT\", \"WHATSAPP\", \"SHOP_AUTOMATIC\", \"ON_AD\", \"ON_POST\", \"ON_VIDEO\", \"ON_PAGE\", \"ON_EVENT\", \"IG_SHOPPING\"],\n },\n {\n key: \"promoted_object\",\n apiField: \"promoted_object\",\n name: \"Promoted Object\",\n description: \"The object being promoted: pixel_id, application_id, page_id, custom_event_type, etc.\",\n category: \"adset\",\n type: \"object\",\n level: [\"adset\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"targeting_optimization_types\",\n apiField: \"targeting_optimization_types\",\n name: \"Targeting Optimization\",\n description: \"Types of targeting optimization enabled: NONE, EXPANSION_ALL, TARGETING_EXPANSION, LOOKALIKE_EXPANSION.\",\n category: \"adset\",\n type: \"array\",\n level: [\"adset\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"attribution_spec\",\n apiField: \"attribution_spec\",\n name: \"Attribution Spec\",\n description: \"Attribution window configuration for this ad set.\",\n category: \"adset\",\n type: \"object\",\n level: [\"adset\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"use_new_app_click\",\n apiField: \"use_new_app_click\",\n name: \"Use New App Click\",\n description: \"Whether the ad set uses new app click attribution.\",\n category: \"adset\",\n type: \"boolean\",\n level: [\"adset\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"source_adset_id\",\n apiField: \"source_adset_id\",\n name: \"Source Ad Set ID\",\n description: \"ID of the original ad set if this was duplicated.\",\n category: \"adset\",\n type: \"string\",\n level: [\"adset\"],\n isIdentifier: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"pacing_type\",\n apiField: \"pacing_type\",\n name: \"Pacing Type\",\n description: \"How the budget is paced: standard, day_parting, no_pacing.\",\n category: \"adset\",\n type: \"array\",\n level: [\"adset\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"lifetime_imps\",\n apiField: \"lifetime_imps\",\n name: \"Lifetime Impressions Cap\",\n description: \"Maximum number of impressions for lifetime. Used for reach objectives.\",\n category: \"adset\",\n type: \"number\",\n level: [\"adset\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"daily_min_spend_target\",\n apiField: \"daily_min_spend_target\",\n name: \"Daily Min Spend Target\",\n description: \"Minimum daily spend target in cents.\",\n category: \"adset\",\n type: \"number\",\n level: [\"adset\"],\n isIdentifier: false,\n isBudget: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"daily_spend_cap\",\n apiField: \"daily_spend_cap\",\n name: \"Daily Spend Cap\",\n description: \"Maximum daily spend in cents.\",\n category: \"adset\",\n type: \"number\",\n level: [\"adset\"],\n isIdentifier: false,\n isBudget: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"lifetime_spend_cap\",\n apiField: \"lifetime_spend_cap\",\n name: \"Lifetime Spend Cap\",\n description: \"Maximum lifetime spend in cents.\",\n category: \"adset\",\n type: \"number\",\n level: [\"adset\"],\n isIdentifier: false,\n isBudget: true,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n\n // ============================================\n // AD DIMENSIONS\n // ============================================\n {\n key: \"ad_id\",\n apiField: \"ad_id\",\n name: \"Ad ID\",\n description: \"Unique numeric ID for the individual ad. Primary key for ad-level data. Links to creative via creative field.\",\n category: \"ad\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: true,\n },\n {\n key: \"ad_name\",\n apiField: \"ad_name\",\n name: \"Ad Name\",\n description: \"The display name you set for the ad. Often contains creative variant information.\",\n category: \"ad\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n },\n {\n key: \"ad_effective_status\",\n apiField: \"effective_status\",\n name: \"Ad Status (Effective)\",\n description: \"Computed status considering parent states: ACTIVE, PAUSED, DELETED, ARCHIVED, CAMPAIGN_PAUSED, ADSET_PAUSED, IN_PROCESS, WITH_ISSUES, PENDING_REVIEW, DISAPPROVED.\",\n category: \"ad\",\n type: \"enum\",\n level: [\"ad\"],\n isIdentifier: false,\n isStatus: true,\n isInsightsField: false, // Requires Ad entity API, not Insights\n enumValues: [\"ACTIVE\", \"PAUSED\", \"DELETED\", \"ARCHIVED\", \"CAMPAIGN_PAUSED\", \"ADSET_PAUSED\", \"IN_PROCESS\", \"WITH_ISSUES\", \"PENDING_REVIEW\", \"DISAPPROVED\", \"PENDING_BILLING_INFO\", \"PREAPPROVED\"],\n },\n {\n key: \"ad_status\",\n apiField: \"status\",\n name: \"Ad Status (Configured)\",\n description: \"The status you configured for the ad: ACTIVE, PAUSED, DELETED, ARCHIVED.\",\n category: \"ad\",\n type: \"enum\",\n level: [\"ad\"],\n isIdentifier: false,\n isStatus: true,\n isInsightsField: false, // Requires Ad entity API, not Insights\n enumValues: [\"ACTIVE\", \"PAUSED\", \"DELETED\", \"ARCHIVED\"],\n },\n {\n key: \"ad_created_time\",\n apiField: \"created_time\",\n name: \"Ad Created\",\n description: \"When the ad was created in ISO 8601 format.\",\n category: \"ad\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n isTimestamp: true,\n isInsightsField: false, // Requires Ad entity API, not Insights\n },\n {\n key: \"ad_updated_time\",\n apiField: \"updated_time\",\n name: \"Ad Updated\",\n description: \"When the ad was last modified in ISO 8601 format.\",\n category: \"ad\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n isTimestamp: true,\n isInsightsField: false, // Requires Ad entity API, not Insights\n },\n {\n key: \"preview_shareable_link\",\n apiField: \"preview_shareable_link\",\n name: \"Preview Link\",\n description: \"Shareable URL to preview the ad before it's live.\",\n category: \"ad\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API, not Insights\n },\n {\n key: \"source_ad_id\",\n apiField: \"source_ad_id\",\n name: \"Source Ad ID\",\n description: \"ID of the original ad if this was duplicated.\",\n category: \"ad\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: true,\n isInsightsField: false, // Requires Ad entity API, not Insights\n },\n {\n key: \"tracking_specs\",\n apiField: \"tracking_specs\",\n name: \"Tracking Specs\",\n description: \"Tracking configuration for the ad including pixels and events.\",\n category: \"ad\",\n type: \"object\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API, not Insights\n },\n {\n key: \"conversion_domain\",\n apiField: \"conversion_domain\",\n name: \"Conversion Domain\",\n description: \"Primary domain for conversion tracking. Used for iOS 14+ attribution.\",\n category: \"ad\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API, not Insights\n },\n {\n key: \"bid_type\",\n apiField: \"bid_type\",\n name: \"Bid Type\",\n description: \"Type of bid for this ad: CPC, CPM, ABSOLUTE_OCPM, etc.\",\n category: \"ad\",\n type: \"enum\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API, not Insights\n enumValues: [\"CPC\", \"CPM\", \"ABSOLUTE_OCPM\", \"MULTI_PREMIUM\"],\n },\n {\n key: \"ad_review_feedback\",\n apiField: \"ad_review_feedback\",\n name: \"Review Feedback\",\n description: \"Feedback from Meta's ad review process if the ad was disapproved or has issues.\",\n category: \"ad\",\n type: \"object\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API, not Insights\n },\n\n // ============================================\n // CREATIVE DIMENSIONS\n // All creative fields require Ad entity API, not Insights\n // ============================================\n {\n key: \"creative_id\",\n apiField: \"creative.id\",\n name: \"Creative ID\",\n description: \"ID of the creative object associated with this ad. Use to fetch full creative details via /adcreatives endpoint.\",\n category: \"creative\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: true,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_name\",\n apiField: \"creative.name\",\n name: \"Creative Name\",\n description: \"Name of the creative object. May be auto-generated if not explicitly set.\",\n category: \"creative\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_title\",\n apiField: \"creative.title\",\n name: \"Creative Title\",\n description: \"The headline/title text of the creative.\",\n category: \"creative\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_body\",\n apiField: \"creative.body\",\n name: \"Creative Body\",\n description: \"The primary text/body copy of the creative.\",\n category: \"creative\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_call_to_action_type\",\n apiField: \"creative.call_to_action_type\",\n name: \"CTA Type\",\n description: \"Call-to-action button type: SHOP_NOW, LEARN_MORE, SIGN_UP, DOWNLOAD, etc.\",\n category: \"creative\",\n type: \"enum\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n enumValues: [\"SHOP_NOW\", \"LEARN_MORE\", \"SIGN_UP\", \"DOWNLOAD\", \"GET_OFFER\", \"CONTACT_US\", \"BOOK_NOW\", \"WATCH_MORE\", \"APPLY_NOW\", \"GET_QUOTE\", \"SUBSCRIBE\", \"DONATE_NOW\", \"ORDER_NOW\", \"ADD_TO_CART\", \"BUY_NOW\", \"MESSAGE_PAGE\", \"WHATSAPP_MESSAGE\", \"CALL_NOW\", \"GET_DIRECTIONS\", \"SEND_MESSAGE\"],\n },\n {\n key: \"creative_link_url\",\n apiField: \"creative.link_url\",\n name: \"Creative Link URL\",\n description: \"The destination URL when users click the creative.\",\n category: \"creative\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_thumbnail_url\",\n apiField: \"creative.thumbnail_url\",\n name: \"Thumbnail URL\",\n description: \"URL of the creative's thumbnail image.\",\n category: \"creative\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_image_url\",\n apiField: \"creative.image_url\",\n name: \"Image URL\",\n description: \"URL of the main image for image creatives.\",\n category: \"creative\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_video_id\",\n apiField: \"creative.video_id\",\n name: \"Video ID\",\n description: \"ID of the video asset for video creatives.\",\n category: \"creative\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: true,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_object_type\",\n apiField: \"creative.object_type\",\n name: \"Creative Object Type\",\n description: \"Type of creative: VIDEO, PHOTO, CAROUSEL, STATUS, OFFER, etc.\",\n category: \"creative\",\n type: \"enum\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n enumValues: [\"APPLICATION\", \"DOMAIN\", \"EVENT\", \"INVALID\", \"OFFER\", \"PAGE\", \"PHOTO\", \"POST_DELETED\", \"PRIVACY_CHECK_FAIL\", \"SHARE\", \"STATUS\", \"STORE_ITEM\", \"VIDEO\"],\n },\n {\n key: \"creative_effective_object_story_id\",\n apiField: \"creative.effective_object_story_id\",\n name: \"Story ID\",\n description: \"ID of the Page post used for the ad, in format 'page_id_post_id'.\",\n category: \"creative\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: true,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_object_story_spec\",\n apiField: \"creative.object_story_spec\",\n name: \"Story Spec\",\n description: \"Specification for creating a new post for the ad.\",\n category: \"creative\",\n type: \"object\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_image_hash\",\n apiField: \"creative.image_hash\",\n name: \"Image Hash\",\n description: \"Hash identifier of the image asset.\",\n category: \"creative\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_instagram_actor_id\",\n apiField: \"creative.instagram_actor_id\",\n name: \"Instagram Actor ID\",\n description: \"ID of the Instagram account used for the ad.\",\n category: \"creative\",\n type: \"string\",\n level: [\"ad\"],\n isIdentifier: true,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_is_dynamic_creative\",\n apiField: \"creative.use_page_actor_override\",\n name: \"Is Dynamic Creative\",\n description: \"Whether this creative uses Dynamic Creative optimization.\",\n category: \"creative\",\n type: \"boolean\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n {\n key: \"creative_asset_feed_spec\",\n apiField: \"creative.asset_feed_spec\",\n name: \"Asset Feed Spec\",\n description: \"Dynamic Creative asset feed specification with multiple images/videos/texts.\",\n category: \"creative\",\n type: \"object\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API with creative expansion\n },\n\n // ============================================\n // TIME DIMENSIONS\n // ============================================\n {\n key: \"date_start\",\n apiField: \"date_start\",\n name: \"Date Start\",\n description: \"First day of the reporting period in YYYY-MM-DD format. Always present in insights response. Use for time-series grouping.\",\n category: \"time\",\n type: \"string\",\n level: [\"account\", \"campaign\", \"adset\", \"ad\"],\n isIdentifier: false,\n isTimestamp: true,\n },\n {\n key: \"date_stop\",\n apiField: \"date_stop\",\n name: \"Date Stop\",\n description: \"Last day of the reporting period in YYYY-MM-DD format. Always present. With date_start, defines the row's time window.\",\n category: \"time\",\n type: \"string\",\n level: [\"account\", \"campaign\", \"adset\", \"ad\"],\n isIdentifier: false,\n isTimestamp: true,\n },\n\n // ============================================\n // LABEL DIMENSIONS\n // All label fields require entity API, not Insights\n // ============================================\n {\n key: \"labels\",\n apiField: \"labels\",\n name: \"Labels\",\n description: \"Custom labels applied for organization. Array of label names. Useful for custom categorization and filtering.\",\n category: \"label\",\n type: \"array\",\n level: [\"campaign\", \"adset\", \"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires entity API, not Insights\n },\n {\n key: \"ad_labels\",\n apiField: \"ad_labels\",\n name: \"Ad Labels\",\n description: \"Detailed ad labels with full metadata including id, name, created_time. More detailed than simple labels array.\",\n category: \"label\",\n type: \"array\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API, not Insights\n },\n {\n key: \"adset_labels\",\n apiField: \"labels\",\n name: \"Ad Set Labels\",\n description: \"Labels applied to the ad set for organization.\",\n category: \"label\",\n type: \"array\",\n level: [\"adset\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n },\n {\n key: \"campaign_labels\",\n apiField: \"labels\",\n name: \"Campaign Labels\",\n description: \"Labels applied to the campaign for organization.\",\n category: \"label\",\n type: \"array\",\n level: [\"campaign\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Campaign entity API, not Insights\n },\n\n // ============================================\n // ATTRIBUTION DIMENSIONS\n // ============================================\n {\n key: \"attribution_setting\",\n apiField: \"attribution_setting\",\n name: \"Attribution Setting\",\n description: \"Current attribution window setting for conversions.\",\n category: \"attribution\",\n type: \"enum\",\n level: [\"adset\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad Set entity API, not Insights\n enumValues: [\"1d_click\", \"7d_click\", \"28d_click\", \"1d_view\", \"7d_view\", \"28d_view\", \"1d_ev\", \"default\"],\n },\n {\n key: \"conversion_specs\",\n apiField: \"conversion_specs\",\n name: \"Conversion Specs\",\n description: \"Specifications for tracked conversions.\",\n category: \"attribution\",\n type: \"object\",\n level: [\"ad\"],\n isIdentifier: false,\n isInsightsField: false, // Requires Ad entity API, not Insights\n },\n {\n key: \"action_attribution_windows\",\n apiField: \"action_attribution_windows\",\n name: \"Action Attribution Windows\",\n description: \"Attribution windows used for this data: 1d_click, 7d_click, 1d_view, etc.\",\n category: \"attribution\",\n type: \"array\",\n level: [\"account\", \"campaign\", \"adset\", \"ad\"],\n isIdentifier: false,\n isInsightsField: false, // This is a request parameter, not a response field\n },\n];\n\n// ============================================\n// HELPER FUNCTIONS\n// ============================================\n\n/**\n * Get dimensions available for a specific insights level\n */\nexport function getDimensionsByLevel(level: InsightsLevel): DimensionDefinition[] {\n return DIMENSION_CATALOG.filter(d => d.level.includes(level));\n}\n\n/**\n * Get only dimensions that are available via the Insights API\n * Filters out entity-only fields (budgets, statuses, timestamps, creatives, etc.)\n */\nexport function getInsightsDimensions(): DimensionDefinition[] {\n return DIMENSION_CATALOG.filter(d => d.isInsightsField !== false);\n}\n\n/**\n * Get insights-compatible dimensions for a specific level\n */\nexport function getInsightsDimensionsByLevel(level: InsightsLevel): DimensionDefinition[] {\n return DIMENSION_CATALOG.filter(d => d.level.includes(level) && d.isInsightsField !== false);\n}\n\n/**\n * Check if a dimension is available via the Insights API\n */\nexport function isDimensionInsightsField(key: string): boolean {\n const dim = DIMENSION_CATALOG.find(d => d.key === key);\n return dim?.isInsightsField !== false;\n}\n\n/**\n * Filter dimension keys to only include insights-compatible fields\n */\nexport function filterInsightsDimensionKeys(keys: string[]): string[] {\n return keys.filter(key => isDimensionInsightsField(key));\n}\n\n/**\n * Get dimensions by category\n */\nexport function getDimensionsByCategory(category: DimensionCategory): DimensionDefinition[] {\n return DIMENSION_CATALOG.filter(d => d.category === category);\n}\n\n/**\n * Get only identifier dimensions (used for joins)\n */\nexport function getIdentifierDimensions(): DimensionDefinition[] {\n return DIMENSION_CATALOG.filter(d => d.isIdentifier);\n}\n\n/**\n * Get only status dimensions\n */\nexport function getStatusDimensions(): DimensionDefinition[] {\n return DIMENSION_CATALOG.filter(d => d.isStatus);\n}\n\n/**\n * Get only budget-related dimensions\n */\nexport function getBudgetDimensions(): DimensionDefinition[] {\n return DIMENSION_CATALOG.filter(d => d.isBudget);\n}\n\n/**\n * Get only timestamp dimensions\n */\nexport function getTimestampDimensions(): DimensionDefinition[] {\n return DIMENSION_CATALOG.filter(d => d.isTimestamp);\n}\n\n/**\n * Convert dimension keys to API field names\n */\nexport function getDimensionApiFields(dimensionKeys: string[]): string[] {\n return dimensionKeys.map(key => {\n const dim = DIMENSION_CATALOG.find(d => d.key === key);\n return dim?.apiField || key;\n });\n}\n\n/**\n * Find a dimension by its key\n */\nexport function getDimensionByKey(key: string): DimensionDefinition | undefined {\n return DIMENSION_CATALOG.find(d => d.key === key);\n}\n\n/**\n * Get ALL dimensions for a specific level (including entity-only fields)\n */\nexport function getAllDimensionsByLevel(level: InsightsLevel): DimensionDefinition[] {\n return DIMENSION_CATALOG.filter(d => d.level.includes(level));\n}\n\n/**\n * Get entity-only dimension keys (fields that require entity API, not available via Insights)\n */\nexport function getEntityOnlyDimensionKeys(dimensionKeys: string[]): string[] {\n return dimensionKeys.filter(key => {\n const dim = DIMENSION_CATALOG.find(d => d.key === key);\n return dim && dim.isInsightsField === false;\n });\n}\n\n/**\n * Get the entity ID field name for a given dimension category\n * Used to know which ID to use when fetching entity metadata\n */\nexport function getEntityIdField(category: DimensionCategory): string | null {\n switch (category) {\n case \"ad\": return \"ad_id\";\n case \"adset\": return \"adset_id\";\n case \"campaign\": return \"campaign_id\";\n case \"account\": return \"account_id\";\n default: return null;\n }\n}\n\n/**\n * Get default dimensions for a level (commonly used identifiers + names)\n */\nexport function getDefaultDimensionsForLevel(level: InsightsLevel): string[] {\n const defaults: Record<InsightsLevel, string[]> = {\n account: [\"account_id\", \"account_name\", \"account_currency\", \"date_start\", \"date_stop\"],\n campaign: [\"campaign_id\", \"campaign_name\", \"objective\", \"campaign_effective_status\", \"date_start\", \"date_stop\"],\n adset: [\"adset_id\", \"adset_name\", \"campaign_id\", \"campaign_name\", \"adset_effective_status\", \"date_start\", \"date_stop\"],\n ad: [\"ad_id\", \"ad_name\", \"adset_id\", \"campaign_id\", \"ad_effective_status\", \"creative_id\", \"date_start\", \"date_stop\"],\n };\n return defaults[level];\n}\n\n/**\n * Category display information for UI\n */\nexport const DIMENSION_CATEGORIES = {\n account: {\n name: \"Account\",\n description: \"Ad account identifiers and settings\",\n icon: \"building\",\n },\n campaign: {\n name: \"Campaign\",\n description: \"Campaign identifiers and configuration\",\n icon: \"target\",\n },\n adset: {\n name: \"Ad Set\",\n description: \"Ad set identifiers, budgets, and targeting settings\",\n icon: \"layers\",\n },\n ad: {\n name: \"Ad\",\n description: \"Individual ad identifiers and status\",\n icon: \"image\",\n },\n creative: {\n name: \"Creative\",\n description: \"Creative content details (text, images, videos)\",\n icon: \"palette\",\n },\n time: {\n name: \"Time\",\n description: \"Date and time period information\",\n icon: \"calendar\",\n },\n label: {\n name: \"Labels\",\n description: \"Custom organization labels\",\n icon: \"tag\",\n },\n attribution: {\n name: \"Attribution\",\n description: \"Conversion attribution settings and windows\",\n icon: \"git-branch\",\n },\n} as const;\n\n// Export count for reference\nexport const DIMENSION_COUNT = DIMENSION_CATALOG.length;\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// META MARKETING API QUERY PLANNER\n// Splits incompatible metrics/breakdowns into separate API calls\n// ============================================\n\nimport {\n InsightsQueryRequest,\n QueryPlan,\n MetaApiRequest,\n InsightsLevel,\n} from \"./types.js\";\nimport { getMetricByKey } from \"./metric-catalog.js\";\nimport {\n validateQuerySelection,\n getApiFieldsForMetrics,\n metricsRequireActionBreakdowns,\n isActionBasedMetric,\n ACTION_BASED_METRICS,\n CREATIVE_BREAKDOWNS,\n CREATIVE_BREAKDOWN_SUPPORTED_METRICS,\n} from \"./compatibility-rules.js\";\nimport { getRequiredCombinations } from \"./breakdown-catalog.js\";\nimport { getDimensionApiFields } from \"./dimension-catalog.js\";\nimport { logger } from \"../../core/logger.js\";\n\n// ============================================\n// QUERY PLANNER\n// ============================================\n\n/**\n * Ensure all required breakdown combinations are included\n * For example: platform_position and impression_device require publisher_platform\n */\nfunction ensureRequiredBreakdowns(breakdowns: string[]): string[] {\n const result = new Set(breakdowns);\n\n for (const breakdown of breakdowns) {\n const required = getRequiredCombinations(breakdown);\n for (const req of required) {\n if (!result.has(req)) {\n result.add(req);\n }\n }\n }\n\n return Array.from(result);\n}\n\n/**\n * Action breakdown keys that should NEVER be in regular breakdowns array\n */\nconst ACTION_BREAKDOWN_KEYS = [\n \"action_type\",\n \"action_device\",\n \"action_destination\",\n \"action_target_id\",\n \"action_reaction\",\n \"action_video_sound\",\n \"action_video_type\",\n \"action_carousel_card_id\",\n \"action_carousel_card_name\",\n \"action_canvas_component_name\",\n \"conversion_destination\",\n \"standard_event_content_type\",\n \"signal_source_bucket\",\n \"is_business_ai_assisted\",\n];\n\n/**\n * Plan queries for an insights request\n * Splits incompatible metrics/breakdowns into separate API calls\n */\nexport function planQueries(request: InsightsQueryRequest): QueryPlan {\n const { metrics, breakdowns: rawBreakdowns = [], actionBreakdowns: rawActionBreakdowns = [], level } = request;\n\n const warnings: string[] = [];\n const errors: string[] = [];\n\n // DEFENSIVE: Ensure action breakdowns are NEVER in regular breakdowns array\n // This fixes issues with persisted state or incorrect UI state\n const cleanedBreakdowns = rawBreakdowns.filter((b) => {\n if (ACTION_BREAKDOWN_KEYS.includes(b)) {\n warnings.push(\n `\"${b}\" is an action breakdown and was removed from regular breakdowns. Use the Action Breakdowns section instead.`\n );\n return false;\n }\n return true;\n });\n\n // DEFENSIVE: Ensure regular breakdowns are NEVER in action breakdowns array\n const cleanedActionBreakdowns = rawActionBreakdowns.filter((b) => {\n if (!ACTION_BREAKDOWN_KEYS.includes(b)) {\n warnings.push(\n `\"${b}\" is not an action breakdown and was removed from action breakdowns.`\n );\n return false;\n }\n return true;\n });\n\n // Ensure required breakdown combinations are included\n // e.g., platform_position and impression_device require publisher_platform\n const breakdowns = ensureRequiredBreakdowns(cleanedBreakdowns);\n const actionBreakdowns = cleanedActionBreakdowns;\n\n // Notify if we added required breakdowns\n if (breakdowns.length > cleanedBreakdowns.length) {\n const added = breakdowns.filter(b => !cleanedBreakdowns.includes(b));\n warnings.push(\n `Auto-added required breakdown(s): ${added.join(\", \")}. Some breakdowns require specific combinations to work.`\n );\n }\n\n // Validate the selection first\n const validation = validateQuerySelection(\n metrics,\n breakdowns,\n actionBreakdowns,\n level\n );\n errors.push(...validation.errors);\n warnings.push(...validation.warnings);\n\n // If there are errors, return early\n if (errors.length > 0) {\n return {\n requests: [],\n calculatedMetrics: [],\n joinKeys: getJoinKeys(level, breakdowns),\n warnings,\n errors,\n };\n }\n\n // Identify calculated metrics\n const calculatedMetricKeys = metrics.filter((m) => {\n const metric = getMetricByKey(m);\n return metric?.type === \"calculated\";\n });\n\n // Get all native metrics needed (including required fields for calculated)\n const nativeMetrics = getNativeMetricsNeeded(metrics);\n\n // Check if we have creative breakdowns\n const hasCreativeBreakdowns = breakdowns.some((b) =>\n CREATIVE_BREAKDOWNS.includes(b)\n );\n\n // Check if we have action breakdowns\n const hasActionBreakdowns = actionBreakdowns.length > 0;\n\n // Build API requests\n const requests: MetaApiRequest[] = [];\n\n if (hasCreativeBreakdowns) {\n // Creative breakdowns have strict limitations\n requests.push(\n ...planCreativeBreakdownRequests(\n nativeMetrics,\n breakdowns,\n actionBreakdowns,\n level,\n request\n )\n );\n } else if (hasActionBreakdowns || metricsRequireActionBreakdowns(nativeMetrics)) {\n // Need to separate delivery metrics from action metrics\n requests.push(\n ...planActionMetricRequests(\n nativeMetrics,\n breakdowns,\n actionBreakdowns,\n level,\n request\n )\n );\n } else {\n // Simple case - all metrics can be fetched together\n const apiFields = getApiFieldsForMetrics(nativeMetrics);\n requests.push(\n createRequest(\"delivery\", apiFields, breakdowns, [], level, request)\n );\n }\n\n return {\n requests,\n calculatedMetrics: calculatedMetricKeys,\n joinKeys: getJoinKeys(level, breakdowns),\n warnings,\n errors,\n };\n}\n\n/**\n * Get all native metrics needed, including required fields for calculated metrics\n */\nfunction getNativeMetricsNeeded(metrics: string[]): string[] {\n const nativeMetrics: Set<string> = new Set();\n\n for (const metricKey of metrics) {\n const metric = getMetricByKey(metricKey);\n if (!metric) continue;\n\n // Skip deprecated metrics - they cause API errors\n // (e.g., video_10_sec_watched_actions is no longer valid in Meta API)\n if (metric.isDeprecated) {\n logger.warn(\"meta\", \"Skipping deprecated metric\", { metricKey });\n continue;\n }\n\n if (metric.type === \"calculated\" && metric.requiredFields) {\n // Add required fields for calculated metrics\n for (const field of metric.requiredFields) {\n nativeMetrics.add(field);\n }\n } else {\n // Add the metric itself\n nativeMetrics.add(metricKey);\n }\n }\n\n return Array.from(nativeMetrics);\n}\n\n/**\n * Plan requests when creative breakdowns are used\n *\n * IMPORTANT: Creative breakdowns (image_asset, video_asset, media_type, etc.)\n * CANNOT be combined with action_type (action_breakdown).\n *\n * Strategy:\n * 1. Creative request: metrics + creative breakdowns (aggregated data per creative asset)\n * 2. Action request: action fields + action_type (NO creative breakdowns) - for action breakdown data\n * 3. Delivery request: non-creative-compatible metrics with non-creative breakdowns\n */\nfunction planCreativeBreakdownRequests(\n metrics: string[],\n breakdowns: string[],\n actionBreakdowns: string[],\n level: InsightsLevel,\n request: InsightsQueryRequest\n): MetaApiRequest[] {\n const requests: MetaApiRequest[] = [];\n\n // Separate creative breakdowns from non-creative breakdowns\n const creativeBreakdowns = breakdowns.filter((b) =>\n CREATIVE_BREAKDOWNS.includes(b)\n );\n const nonCreativeBreakdowns = breakdowns.filter(\n (b) => !CREATIVE_BREAKDOWNS.includes(b)\n );\n\n // Separate metrics into those compatible with creative breakdowns and those that aren't\n const creativeCompatibleMetrics = metrics.filter((m) =>\n CREATIVE_BREAKDOWN_SUPPORTED_METRICS.includes(m)\n );\n const creativeIncompatibleMetrics = metrics.filter(\n (m) => !CREATIVE_BREAKDOWN_SUPPORTED_METRICS.includes(m)\n );\n\n // Identify if we have action-based metrics that need action_type breakdown\n // Use isActionBasedMetric() to check both direct action fields AND metrics that extract from action arrays\n // (e.g., \"purchases\" extracts from actions[action_type=\"offsite_conversion.fb_pixel_purchase\"])\n const hasActionMetrics = metrics.some((m) => isActionBasedMetric(m));\n const needsActionBreakdown = actionBreakdowns.length > 0 || hasActionMetrics;\n\n // Request 1: Creative-compatible metrics with creative breakdowns ONLY\n // This gives us aggregated data per creative asset (no action_type breakdown)\n if (creativeCompatibleMetrics.length > 0 && creativeBreakdowns.length > 0) {\n // Remove action-based metrics from creative request if we'll have a separate action request\n // This avoids duplicate data and confusion\n const creativeOnlyMetrics = needsActionBreakdown\n ? creativeCompatibleMetrics.filter((m) => !ACTION_BASED_METRICS.includes(m))\n : creativeCompatibleMetrics;\n\n if (creativeOnlyMetrics.length > 0) {\n const apiFields = getApiFieldsForMetrics(creativeOnlyMetrics);\n requests.push(\n createRequest(\"creative\", apiFields, creativeBreakdowns, [], level, request)\n );\n }\n }\n\n // Request 2: Action metrics\n // When creative breakdowns are used, action metrics need special handling:\n // - Creative breakdowns (image_asset, video_asset) CANNOT be combined with action_type\n // - But NON-creative breakdowns CAN be included if no explicit action_breakdowns\n if (needsActionBreakdown) {\n const actionFields = getApiFieldsForMetrics([\n \"actions\",\n \"action_values\",\n \"cost_per_action_type\",\n ]);\n\n if (actionBreakdowns.length > 0) {\n // User explicitly wants action breakdowns - can't combine with any breakdowns\n requests.push(\n createRequest(\n \"actions\",\n actionFields,\n [], // No breakdowns - entity level data\n actionBreakdowns,\n level,\n request\n )\n );\n } else if (nonCreativeBreakdowns.length > 0) {\n // Include non-creative breakdowns (age, gender, etc.) for proper breakdown data\n requests.push(\n createRequest(\n \"actions\",\n actionFields,\n nonCreativeBreakdowns, // Include regular breakdowns\n [], // No action_breakdowns - prevents implicit action_type\n level,\n request\n )\n );\n } else {\n // No breakdowns at all - just get entity level action data\n requests.push(\n createRequest(\n \"actions\",\n actionFields,\n [],\n [], // No action_breakdowns\n level,\n request\n )\n );\n }\n }\n\n // Request 3: Non-creative-compatible metrics with non-creative breakdowns\n // Separate delivery metrics from action metrics\n // IMPORTANT: Include \"video\" category - video metrics are NOT action-based,\n // they return as direct fields (AdsActionStats[]) like video_p25_watched_actions\n const deliveryMetrics = creativeIncompatibleMetrics.filter((m) => {\n const metric = getMetricByKey(m);\n return (\n metric?.category === \"delivery\" ||\n metric?.category === \"cost\" ||\n metric?.category === \"engagement\" ||\n metric?.category === \"video\" // Video metrics are NOT action-based!\n );\n });\n\n // Request for delivery metrics WITH non-creative breakdowns\n // IMPORTANT: When breakdowns are selected, we MUST always send a delivery request\n // to get the breakdown structure in the response, even if no delivery metrics were selected\n if (nonCreativeBreakdowns.length > 0) {\n // Always send delivery request when breakdowns are selected\n const metricsToRequest = deliveryMetrics.length > 0\n ? deliveryMetrics\n : [\"impressions\"]; // Fallback to impressions to get breakdown rows\n\n const deliveryFields = getApiFieldsForMetrics(metricsToRequest);\n requests.push(\n createRequest(\n \"delivery\",\n deliveryFields,\n nonCreativeBreakdowns,\n [],\n level,\n request\n )\n );\n } else if (deliveryMetrics.length > 0) {\n // No breakdowns at all, but we have delivery metrics - send them\n const deliveryFields = getApiFieldsForMetrics(deliveryMetrics);\n requests.push(\n createRequest(\"delivery\", deliveryFields, [], [], level, request)\n );\n }\n\n return requests;\n}\n\n/**\n * Breakdowns that CAN be combined with action_type (action_breakdowns)\n * According to Meta API documentation, these are the only valid combinations\n */\n/**\n * Plan requests when action metrics or action breakdowns are needed\n *\n * IMPORTANT: action_type (action_breakdown) CANNOT be combined with most regular breakdowns.\n * The API will return error: \"Current combination of data breakdown columns is invalid\"\n *\n * Strategy:\n * 1. Delivery metrics WITH regular breakdowns (no action_breakdowns)\n * 2. Action metrics WITHOUT regular breakdowns (action data aggregated at entity level)\n */\nfunction planActionMetricRequests(\n metrics: string[],\n breakdowns: string[],\n actionBreakdowns: string[],\n level: InsightsLevel,\n request: InsightsQueryRequest\n): MetaApiRequest[] {\n const requests: MetaApiRequest[] = [];\n\n // Separate delivery metrics from action-based metrics\n // IMPORTANT: Video metrics are NOT action-based! They return as direct fields\n // (AdsActionStats[] like video_p25_watched_actions), not via the actions array\n const deliveryMetrics = metrics.filter((m) => {\n const metric = getMetricByKey(m);\n return (\n metric?.category === \"delivery\" ||\n metric?.category === \"cost\" ||\n metric?.category === \"engagement\" ||\n metric?.category === \"video\" // Video metrics should be in delivery request!\n );\n });\n\n const actionMetrics = metrics.filter((m) => {\n const metric = getMetricByKey(m);\n // Use isActionBasedMetric() to properly detect all action-based metrics\n // including those that extract from action arrays (e.g., \"purchases\")\n // NOTE: Video metrics are NOT action-based despite returning AdsActionStats[]\n return (\n isActionBasedMetric(m) ||\n metric?.category === \"conversion\"\n );\n });\n\n // Request 1: Delivery metrics with standard breakdowns (NO action_breakdowns)\n // IMPORTANT: When breakdowns are selected, we MUST always send a delivery request\n // to get the breakdown structure in the response. If no delivery metrics were selected,\n // include at least \"impressions\" to get the breakdown rows.\n if (breakdowns.length > 0) {\n // Always send delivery request when breakdowns are selected\n const metricsToRequest = deliveryMetrics.length > 0\n ? deliveryMetrics\n : [\"impressions\"]; // Fallback to impressions to get breakdown rows\n\n const apiFields = getApiFieldsForMetrics(metricsToRequest);\n requests.push(\n createRequest(\"delivery\", apiFields, breakdowns, [], level, request)\n );\n } else if (deliveryMetrics.length > 0) {\n // No breakdowns, but we have delivery metrics - send them\n const apiFields = getApiFieldsForMetrics(deliveryMetrics);\n requests.push(\n createRequest(\"delivery\", apiFields, [], [], level, request)\n );\n }\n\n // Request 2: Action metrics\n // UPDATED RULE: When user has NOT explicitly selected action_breakdowns,\n // we CAN include regular breakdowns with action metrics.\n // The key insight is:\n // - action_type is implicitly added ONLY if action_breakdowns param is absent\n // - If we explicitly pass action_breakdowns as empty [], Meta won't add action_type\n // - This allows combining regular breakdowns (age, gender) with action metrics\n //\n // When user HAS explicitly selected action_breakdowns (like action_device),\n // we CANNOT combine with regular breakdowns - use entity-level merge instead.\n if (actionMetrics.length > 0) {\n const apiFields = getApiFieldsForMetrics(actionMetrics);\n\n const hasExplicitActionBreakdowns = actionBreakdowns.length > 0;\n\n if (hasExplicitActionBreakdowns) {\n // User explicitly wants action breakdowns - can't combine with regular breakdowns\n // Request WITHOUT regular breakdowns, merge at entity level\n requests.push(\n createRequest(\n \"actions\",\n apiFields,\n [], // No regular breakdowns - will be merged at entity level\n actionBreakdowns,\n level,\n request\n )\n );\n } else {\n // No explicit action breakdowns - include regular breakdowns for proper data\n // This gives us action data broken down by age, gender, etc.\n requests.push(\n createRequest(\n \"actions\",\n apiFields,\n breakdowns, // Include regular breakdowns for breakdown-level data\n [], // Empty action_breakdowns - prevents implicit action_type\n level,\n request\n )\n );\n }\n }\n\n return requests;\n}\n\n/**\n * Create a Meta API request object\n * Includes validation to ensure creative breakdowns and action breakdowns are never combined\n */\nfunction createRequest(\n type: \"delivery\" | \"actions\" | \"video\" | \"creative\",\n fields: string[],\n breakdowns: string[],\n actionBreakdowns: string[],\n level: InsightsLevel,\n request: InsightsQueryRequest\n): MetaApiRequest {\n // CRITICAL VALIDATION: Creative breakdowns and action breakdowns CANNOT be combined\n // Meta API validates ALL breakdown dimensions together and will reject invalid combinations\n const hasCreativeBreakdowns = breakdowns.some((b) => CREATIVE_BREAKDOWNS.includes(b));\n const hasActionBreakdowns = actionBreakdowns.length > 0;\n\n if (hasCreativeBreakdowns && hasActionBreakdowns) {\n // This should never happen if query planner is working correctly\n // Log error for debugging and strip action breakdowns to prevent API error\n console.error(\n \"[QueryPlanner] CRITICAL: Creative breakdowns combined with action breakdowns!\",\n { breakdowns, actionBreakdowns, type }\n );\n // Strip action breakdowns from this request - they will be in a separate request\n actionBreakdowns = [];\n }\n\n // Also validate that action breakdowns aren't in the regular breakdowns array\n const cleanedBreakdowns = breakdowns.filter((b) => !ACTION_BREAKDOWN_KEYS.includes(b));\n if (cleanedBreakdowns.length !== breakdowns.length) {\n console.error(\n \"[QueryPlanner] Action breakdowns found in regular breakdowns array!\",\n { original: breakdowns, cleaned: cleanedBreakdowns }\n );\n }\n\n // Always include identifiers based on level\n const identifierFields = getIdentifierFields(level);\n\n // Convert dimension keys to API fields and include them\n const dimensionFields = request.dimensions ? getDimensionApiFields(request.dimensions) : [];\n\n const allFields = [...new Set([...identifierFields, ...dimensionFields, ...fields])];\n\n // Build attribution windows from user settings\n let attributionWindows: string[] | undefined;\n if (request.attribution) {\n const windows: string[] = [request.attribution.clickWindow];\n if (request.attribution.includeViewThrough) {\n windows.push(request.attribution.viewWindow);\n }\n attributionWindows = windows;\n }\n\n return {\n type,\n fields: allFields,\n breakdowns: cleanedBreakdowns.length > 0 ? cleanedBreakdowns : undefined,\n actionBreakdowns: actionBreakdowns.length > 0 ? actionBreakdowns : undefined,\n level,\n timeRange: request.timeRange,\n datePreset: request.datePreset,\n timeIncrement: request.timeIncrement,\n limit: request.limit,\n attributionWindows,\n };\n}\n\n/**\n * Get identifier fields based on level\n */\nfunction getIdentifierFields(level: InsightsLevel): string[] {\n const base = [\"date_start\", \"date_stop\", \"account_id\"];\n\n switch (level) {\n case \"ad\":\n return [...base, \"ad_id\", \"ad_name\", \"adset_id\", \"adset_name\", \"campaign_id\", \"campaign_name\"];\n case \"adset\":\n return [...base, \"adset_id\", \"adset_name\", \"campaign_id\", \"campaign_name\"];\n case \"campaign\":\n return [...base, \"campaign_id\", \"campaign_name\"];\n case \"account\":\n return [...base, \"account_name\"];\n default:\n return base;\n }\n}\n\n/**\n * Get join keys for merging results\n */\nfunction getJoinKeys(level: InsightsLevel, breakdowns: string[]): string[] {\n const keys: string[] = [\"date_start\", \"date_stop\"];\n\n // Add level-specific ID\n switch (level) {\n case \"ad\":\n keys.push(\"ad_id\");\n break;\n case \"adset\":\n keys.push(\"adset_id\");\n break;\n case \"campaign\":\n keys.push(\"campaign_id\");\n break;\n case \"account\":\n keys.push(\"account_id\");\n break;\n }\n\n // Add breakdown fields as join keys\n for (const breakdown of breakdowns) {\n // Creative breakdowns return objects, so use their ID as key\n if (CREATIVE_BREAKDOWNS.includes(breakdown)) {\n // These will be handled specially during merge\n keys.push(breakdown);\n } else {\n keys.push(breakdown);\n }\n }\n\n return keys;\n}\n\n// ============================================\n// QUERY EXECUTION HELPERS\n// ============================================\n\n/**\n * Build the API URL for a Meta API request\n */\nexport function buildApiUrl(\n adAccountId: string,\n request: MetaApiRequest,\n apiVersion: string = \"v26.0\"\n): string {\n const baseUrl = `https://graph.facebook.com/${apiVersion}/${adAccountId}/insights`;\n const params = new URLSearchParams();\n\n // Fields\n params.append(\"fields\", request.fields.join(\",\"));\n\n // Level\n params.append(\"level\", request.level);\n\n // Time range or preset\n if (request.timeRange) {\n params.append(\n \"time_range\",\n JSON.stringify({\n since: request.timeRange.since,\n until: request.timeRange.until,\n })\n );\n } else if (request.datePreset) {\n params.append(\"date_preset\", request.datePreset);\n }\n\n // Time increment\n if (request.timeIncrement) {\n params.append(\"time_increment\", String(request.timeIncrement));\n }\n\n // Breakdowns\n if (request.breakdowns && request.breakdowns.length > 0) {\n params.append(\"breakdowns\", request.breakdowns.join(\",\"));\n }\n\n // Action breakdowns\n if (request.actionBreakdowns && request.actionBreakdowns.length > 0) {\n params.append(\"action_breakdowns\", request.actionBreakdowns.join(\",\"));\n }\n\n // Limit\n if (request.limit) {\n params.append(\"limit\", String(request.limit));\n }\n\n return `${baseUrl}?${params.toString()}`;\n}\n\n/**\n * Build fetch options for Meta API request\n */\nexport function buildFetchOptions(accessToken: string): RequestInit {\n return {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n };\n}\n\n// ============================================\n// RESULT MERGING\n// ============================================\n\n/**\n * Get base entity join keys (without breakdowns)\n * These keys identify the entity (ad, adset, campaign, account) + time period\n */\nfunction getBaseEntityKeys(row: Record<string, unknown>): string[] {\n const entityKeys: string[] = [];\n\n // Time period\n if (row.date_start) entityKeys.push(\"date_start\");\n if (row.date_stop) entityKeys.push(\"date_stop\");\n\n // Entity identifiers (in order of specificity)\n if (row.ad_id) entityKeys.push(\"ad_id\");\n else if (row.adset_id) entityKeys.push(\"adset_id\");\n else if (row.campaign_id) entityKeys.push(\"campaign_id\");\n else if (row.account_id) entityKeys.push(\"account_id\");\n\n return entityKeys;\n}\n\n/**\n * Merge results from multiple API requests\n *\n * Handles the case where action data (at entity level) needs to be joined\n * with creative breakdown data (at asset level). The action data is \"broadcast\"\n * to all matching creative asset rows.\n */\nexport function mergeResults(\n results: Record<string, unknown>[][],\n joinKeys: string[]\n): Record<string, unknown>[] {\n if (results.length === 0) return [];\n if (results.length === 1) return results[0];\n\n logger.debug(\"meta\", \"Merge starting\", {\n resultSetCount: results.length,\n rowCounts: results.map((r, i) => `Set ${i}: ${r.length} rows`),\n joinKeys,\n });\n\n // Find the result set with the most rows (usually the one with breakdowns)\n // This will be our \"base\" for the merge\n let baseIndex = 0;\n let maxRows = results[0].length;\n\n for (let i = 1; i < results.length; i++) {\n if (results[i].length > maxRows) {\n maxRows = results[i].length;\n baseIndex = i;\n }\n }\n\n logger.debug(\"meta\", \"Merge using base index\", { baseIndex, rowCount: maxRows });\n\n // Create a map for the base result set (with full join keys)\n const mergedMap = new Map<string, Record<string, unknown>>();\n\n // Index base result set by full join keys\n for (const row of results[baseIndex]) {\n const key = generateJoinKey(row, joinKeys);\n mergedMap.set(key, { ...row });\n }\n\n // Debug: show sample keys from base\n const baseKeys = Array.from(mergedMap.keys()).slice(0, 3);\n logger.debug(\"meta\", \"Merge sample base keys\", { baseKeys });\n\n // Create an entity-level index for \"broadcast\" joins\n // This maps entity keys (ad_id + date) to all full keys that match\n const entityToFullKeys = new Map<string, string[]>();\n for (const row of results[baseIndex]) {\n const baseEntityKeys = getBaseEntityKeys(row);\n const entityKey = generateJoinKey(row, baseEntityKeys);\n const fullKey = generateJoinKey(row, joinKeys);\n\n if (!entityToFullKeys.has(entityKey)) {\n entityToFullKeys.set(entityKey, []);\n }\n entityToFullKeys.get(entityKey)!.push(fullKey);\n }\n\n // Merge other result sets\n let exactMatches = 0;\n let broadcastMatches = 0;\n let noMatches = 0;\n\n for (let i = 0; i < results.length; i++) {\n if (i === baseIndex) continue;\n\n // Debug: show sample keys from this result set\n if (results[i].length > 0) {\n const sampleRow = results[i][0];\n const sampleKey = generateJoinKey(sampleRow, joinKeys);\n logger.debug(\"meta\", \"Merge result set sample\", {\n resultSet: i,\n sampleKey,\n sampleRowKeys: Object.keys(sampleRow).filter(k => joinKeys.includes(k)).map(k => `${k}=${sampleRow[k]}`),\n });\n }\n\n for (const row of results[i]) {\n const fullKey = generateJoinKey(row, joinKeys);\n const existing = mergedMap.get(fullKey);\n\n if (existing) {\n // Exact match - merge fields\n exactMatches++;\n Object.assign(existing, row);\n } else {\n // No exact match - log the first few non-matches for debugging\n if (noMatches + broadcastMatches < 3) {\n logger.debug(\"meta\", \"Merge no exact match\", {\n fullKey,\n rowBreakdownValues: joinKeys.map(k => `${k}=${row[k]}`).join(\", \"),\n });\n }\n\n // Check if this row has breakdown values (age, gender, etc.)\n // If it does, we should NOT broadcast - the data should only go to the exact matching row\n const regularBreakdownKeys = [\"age\", \"gender\", \"country\", \"region\", \"dma\", \"publisher_platform\", \"platform_position\", \"device_platform\", \"impression_device\"];\n const rowHasBreakdowns = regularBreakdownKeys.some(k => row[k] !== undefined && row[k] !== \"\");\n\n // No exact match - try entity-level match (for action data broadcast)\n const baseEntityKeys = getBaseEntityKeys(row);\n const entityKey = generateJoinKey(row, baseEntityKeys);\n const matchingFullKeys = entityToFullKeys.get(entityKey);\n\n if (matchingFullKeys && matchingFullKeys.length > 0) {\n // If the row has breakdowns but no exact match, try to find a row with matching breakdowns\n // This handles cases where the breakdown values might be slightly different (whitespace, etc.)\n if (rowHasBreakdowns) {\n // Try to find a matching row by comparing breakdown values more flexibly\n let foundMatch = false;\n for (const matchKey of matchingFullKeys) {\n const targetRow = mergedMap.get(matchKey);\n if (targetRow) {\n // Check if all breakdown values match (with normalization)\n const allBreakdownsMatch = regularBreakdownKeys.every(k => {\n const rowVal = String(row[k] ?? \"\").trim().toLowerCase();\n const targetVal = String(targetRow[k] ?? \"\").trim().toLowerCase();\n // Both undefined/empty OR both have the same normalized value\n return (rowVal === \"\" && targetVal === \"\") || rowVal === targetVal;\n });\n\n if (allBreakdownsMatch) {\n // Found a match - merge all fields\n Object.assign(targetRow, row);\n foundMatch = true;\n exactMatches++; // Count as exact match since we found the right row\n break;\n }\n }\n }\n\n if (!foundMatch) {\n // Row has breakdowns but no matching row found - add as new row\n // This can happen if the action data has breakdowns not present in delivery\n logger.debug(\"meta\", \"Merge row has breakdowns but no match found - adding as new row\");\n noMatches++;\n mergedMap.set(fullKey, { ...row });\n }\n } else {\n // Row has NO breakdowns - broadcast to all matching entity rows\n // This is the correct behavior for entity-level action data\n broadcastMatches++;\n logger.debug(\"meta\", \"Merge broadcasting action data\", {\n targetRowCount: matchingFullKeys.length,\n entityKey,\n });\n for (const matchKey of matchingFullKeys) {\n const targetRow = mergedMap.get(matchKey);\n if (targetRow) {\n // Merge action-based fields (these are the fields that come from action requests)\n // We copy all action/conversion related fields to each creative asset row\n for (const [field, value] of Object.entries(row)) {\n // Skip identifier fields that shouldn't be overwritten\n if (field.endsWith(\"_id\") || field.endsWith(\"_name\") ||\n field === \"date_start\" || field === \"date_stop\" ||\n field === \"account_currency\") {\n continue;\n }\n\n // Action-based fields to broadcast:\n // - actions, action_values (main action arrays)\n // - conversions, conversion_values\n // - cost_per_action_type, cost_per_conversion\n // - purchase_roas, website_purchase_roas, mobile_app_purchase_roas\n // - outbound_clicks, unique_outbound_clicks, outbound_clicks_ctr\n // - video_*_actions fields\n // - cost_per_thruplay, cost_per_outbound_click\n const isActionField =\n field === \"actions\" || field === \"action_values\" ||\n field === \"conversions\" || field === \"conversion_values\" ||\n field === \"cost_per_action_type\" || field === \"cost_per_conversion\" ||\n field === \"purchase_roas\" || field === \"website_purchase_roas\" || field === \"mobile_app_purchase_roas\" ||\n field === \"outbound_clicks\" || field === \"unique_outbound_clicks\" ||\n field === \"outbound_clicks_ctr\" || field === \"unique_outbound_clicks_ctr\" ||\n field === \"cost_per_outbound_click\" || field === \"cost_per_unique_outbound_click\" ||\n field === \"cost_per_thruplay\" ||\n field.startsWith(\"video_\") && field.endsWith(\"_actions\");\n\n if (isActionField) {\n targetRow[field] = value;\n }\n }\n }\n }\n }\n } else {\n // No entity match - add as new row\n noMatches++;\n mergedMap.set(fullKey, { ...row });\n }\n }\n }\n }\n\n logger.debug(\"meta\", \"Merge complete\", {\n exactMatches,\n broadcastMatches,\n noMatches,\n finalRowCount: mergedMap.size,\n });\n\n return Array.from(mergedMap.values());\n}\n\n/**\n * Generate a join key from a row\n * @param row - The data row\n * @param joinKeys - Keys to use for the join\n * @param includeBreakdowns - If false, only include entity keys (id + date), not breakdown keys\n */\nfunction generateJoinKey(\n row: Record<string, unknown>,\n joinKeys: string[],\n includeBreakdowns: boolean = true\n): string {\n const parts: string[] = [];\n\n // Define which keys are breakdowns (not entity identifiers)\n const breakdownKeys = [\"age\", \"gender\", \"country\", \"region\", \"dma\", \"publisher_platform\", \"platform_position\", \"device_platform\", \"impression_device\"];\n\n for (const key of joinKeys) {\n // Skip breakdown keys if includeBreakdowns is false\n if (!includeBreakdowns && breakdownKeys.includes(key)) {\n continue;\n }\n\n const value = row[key];\n\n // Handle object values (like creative asset breakdowns)\n if (value && typeof value === \"object\") {\n if (\"id\" in value) {\n parts.push(String((value as { id: string }).id));\n } else {\n parts.push(JSON.stringify(value));\n }\n } else {\n // Normalize string values (trim whitespace, handle undefined)\n const strValue = String(value ?? \"\").trim();\n parts.push(strValue);\n }\n }\n\n return parts.join(\"|\");\n}\n\n// ============================================\n// QUERY PLAN DISPLAY HELPERS\n// ============================================\n\n/**\n * Format a query plan for display\n */\nexport function formatQueryPlanForDisplay(plan: QueryPlan): string {\n const lines: string[] = [];\n\n lines.push(`=== Query Plan ===`);\n lines.push(`Requests: ${plan.requests.length}`);\n lines.push(`Calculated Metrics: ${plan.calculatedMetrics.join(\", \") || \"none\"}`);\n lines.push(`Join Keys: ${plan.joinKeys.join(\", \")}`);\n\n if (plan.warnings.length > 0) {\n lines.push(`\\nWarnings:`);\n plan.warnings.forEach((w) => lines.push(` ⚠️ ${w}`));\n }\n\n if (plan.errors.length > 0) {\n lines.push(`\\nErrors:`);\n plan.errors.forEach((e) => lines.push(` ❌ ${e}`));\n }\n\n lines.push(`\\n--- Requests ---`);\n\n plan.requests.forEach((req, index) => {\n lines.push(`\\nRequest ${index + 1} (${req.type}):`);\n lines.push(` Fields: ${req.fields.join(\", \")}`);\n lines.push(` Level: ${req.level}`);\n if (req.breakdowns) {\n lines.push(` Breakdowns: ${req.breakdowns.join(\", \")}`);\n }\n if (req.actionBreakdowns) {\n lines.push(` Action Breakdowns: ${req.actionBreakdowns.join(\", \")}`);\n }\n if (req.timeRange) {\n lines.push(` Time Range: ${req.timeRange.since} to ${req.timeRange.until}`);\n }\n if (req.datePreset) {\n lines.push(` Date Preset: ${req.datePreset}`);\n }\n });\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Get a summary of the query plan\n */\nexport function getQueryPlanSummary(plan: QueryPlan): {\n requestCount: number;\n hasCalculatedMetrics: boolean;\n isValid: boolean;\n summary: string;\n} {\n const isValid = plan.errors.length === 0;\n const hasCalculatedMetrics = plan.calculatedMetrics.length > 0;\n\n let summary = \"\";\n if (!isValid) {\n summary = `Invalid query: ${plan.errors[0]}`;\n } else if (plan.requests.length === 1) {\n summary = \"Single API request - all metrics compatible\";\n } else {\n const types = plan.requests.map((r) => r.type).join(\" + \");\n summary = `${plan.requests.length} requests needed (${types})`;\n if (hasCalculatedMetrics) {\n summary += ` + ${plan.calculatedMetrics.length} calculated`;\n }\n }\n\n return {\n requestCount: plan.requests.length,\n hasCalculatedMetrics,\n isValid,\n summary,\n };\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// META MARKETING API TYPES FOR BENLY REPORTS\n// ============================================\n\n// --- Configuration ---\n\nexport interface MetaApiConfig {\n accessToken: string;\n adAccountId: string; // Format: \"act_123456789\"\n apiVersion: string; // \"v26.0\"\n}\n\nexport interface TimeRange {\n since: string; // \"YYYY-MM-DD\"\n until: string; // \"YYYY-MM-DD\"\n}\n\nexport type DatePreset =\n | \"today\"\n | \"yesterday\"\n | \"this_month\"\n | \"last_month\"\n | \"this_quarter\"\n | \"maximum\"\n | \"data_maximum\"\n | \"last_3d\"\n | \"last_7d\"\n | \"last_14d\"\n | \"last_28d\"\n | \"last_30d\"\n | \"last_90d\"\n | \"last_week_mon_sun\"\n | \"last_week_sun_sat\"\n | \"last_quarter\"\n | \"last_year\"\n | \"this_week_mon_today\"\n | \"this_week_sun_today\"\n | \"this_year\";\n\nexport type InsightsLevel = \"ad\" | \"adset\" | \"campaign\" | \"account\";\n\nexport type TimeIncrement = 1 | 7 | 14 | 28 | 30 | \"monthly\" | \"all_days\";\n\n// --- Attribution Windows ---\n\n/**\n * Attribution window for click-through conversions\n * - 1d_click: Conversions within 1 day of click\n * - 7d_click: Conversions within 7 days of click (default)\n * - 28d_click: Conversions within 28 days of click\n */\nexport type ClickAttributionWindow = \"1d_click\" | \"7d_click\" | \"28d_click\";\n\n/**\n * Attribution window for view-through conversions\n * - 1d_view: Conversions within 1 day of view (no click)\n * - 1d_ev: Conversions within 1 day of engaged video view (10+ seconds)\n * Note: 7d_view and 28d_view are DEPRECATED as of Jan 2026\n */\nexport type ViewAttributionWindow = \"1d_view\" | \"1d_ev\";\n\n/**\n * Combined attribution window settings\n */\nexport interface AttributionSettings {\n /** Click attribution window (default: 7d_click) */\n clickWindow: ClickAttributionWindow;\n /** View attribution window (default: 1d_view) */\n viewWindow: ViewAttributionWindow;\n /** Whether to include view-through conversions (default: true) */\n includeViewThrough: boolean;\n}\n\n/**\n * Display mode for attribution data\n * - combined: Show total (click + view) - default\n * - click_only: Show only click-through conversions\n * - view_only: Show only view-through conversions\n * - split: Show click, view, and total in separate columns\n */\nexport type AttributionDisplayMode = \"combined\" | \"click_only\" | \"view_only\" | \"split\";\n\n/**\n * Preset attribution configurations matching Meta Ads Manager\n */\nexport const ATTRIBUTION_PRESETS = {\n /** Default: 7-day click + 1-day view */\n default: {\n clickWindow: \"7d_click\" as ClickAttributionWindow,\n viewWindow: \"1d_view\" as ViewAttributionWindow,\n includeViewThrough: true,\n },\n /** Click only: 7-day click, no view */\n click_7d: {\n clickWindow: \"7d_click\" as ClickAttributionWindow,\n viewWindow: \"1d_view\" as ViewAttributionWindow,\n includeViewThrough: false,\n },\n /** Click only: 1-day click, no view */\n click_1d: {\n clickWindow: \"1d_click\" as ClickAttributionWindow,\n viewWindow: \"1d_view\" as ViewAttributionWindow,\n includeViewThrough: false,\n },\n /** Click only: 28-day click, no view */\n click_28d: {\n clickWindow: \"28d_click\" as ClickAttributionWindow,\n viewWindow: \"1d_view\" as ViewAttributionWindow,\n includeViewThrough: false,\n },\n /** Engaged view: 7-day click + 1-day engaged view (video) */\n engaged_view: {\n clickWindow: \"7d_click\" as ClickAttributionWindow,\n viewWindow: \"1d_ev\" as ViewAttributionWindow,\n includeViewThrough: true,\n },\n} as const;\n\nexport type AttributionPreset = keyof typeof ATTRIBUTION_PRESETS;\n\n// --- Metric Categories ---\n\nexport type MetricCategory =\n | \"delivery\"\n | \"cost\"\n | \"video\"\n | \"conversion\"\n | \"engagement\"\n | \"calculated\";\n\nexport type MetricType = \"native\" | \"calculated\";\n\nexport type MetricFormat =\n | \"number\"\n | \"currency\"\n | \"percentage\"\n | \"duration\"\n | \"ratio\";\n\n// --- Breakdown Categories ---\n\nexport type BreakdownCategory =\n | \"demographic\"\n | \"audience\"\n | \"geographic\"\n | \"platform\"\n | \"creative\"\n | \"product\"\n | \"time\"\n | \"action\"\n | \"skan\";\n\n// --- Breakdowns ---\n\nexport type DemographicBreakdown = \"age\" | \"gender\";\n\nexport type GeographicBreakdown = \"country\" | \"region\" | \"dma\";\n\nexport type PlatformBreakdown =\n | \"publisher_platform\"\n | \"platform_position\"\n | \"device_platform\"\n | \"impression_device\";\n\nexport type CreativeBreakdown =\n | \"image_asset\"\n | \"video_asset\"\n | \"body_asset\"\n | \"title_asset\"\n | \"description_asset\"\n | \"call_to_action_asset\"\n | \"link_url_asset\"\n | \"ad_format_asset\"\n | \"media_type\"\n | \"media_format\"\n | \"flexible_format_asset_type\"\n | \"gen_ai_asset_type\";\n\nexport type ProductBreakdown =\n | \"product_id\"\n | \"product_brand_breakdown\"\n | \"product_category_breakdown\"\n | \"product_type_breakdown\"\n | \"product_custom_label_0_breakdown\"\n | \"product_custom_label_1_breakdown\"\n | \"product_custom_label_2_breakdown\"\n | \"product_custom_label_3_breakdown\"\n | \"product_custom_label_4_breakdown\"\n | \"product_group_content_id_breakdown\";\n\nexport type TimeBreakdown =\n | \"hourly_stats_aggregated_by_advertiser_time_zone\"\n | \"hourly_stats_aggregated_by_audience_time_zone\"\n | \"frequency_value\";\n\nexport type ActionBreakdown =\n | \"action_type\"\n | \"action_device\"\n | \"action_destination\"\n | \"action_target_id\"\n | \"action_reaction\"\n | \"action_video_sound\"\n | \"action_video_type\"\n | \"action_carousel_card_id\"\n | \"action_carousel_card_name\"\n | \"action_canvas_component_name\"\n | \"conversion_destination\"\n | \"standard_event_content_type\"\n | \"signal_source_bucket\"\n | \"is_business_ai_assisted\";\n\nexport type SkanBreakdown =\n | \"skan_campaign_id\"\n | \"skan_conversion_id\"\n | \"skan_version\"\n | \"coarse_conversion_value\"\n | \"postback_sequence_index\"\n | \"fidelity_type\"\n | \"redownload\";\n\nexport type Breakdown =\n | DemographicBreakdown\n | GeographicBreakdown\n | PlatformBreakdown\n | CreativeBreakdown\n | ProductBreakdown\n | TimeBreakdown;\n\n// --- Catalog Definitions ---\n\nexport interface MetricDefinition {\n key: string;\n name: string;\n description: string;\n category: MetricCategory;\n type: MetricType;\n format: MetricFormat;\n apiField?: string; // Native field name if different from key\n compatibleBreakdowns: string[];\n incompatibleBreakdowns?: string[];\n requiredFields?: string[]; // For calculated metrics\n levelRestrictions?: InsightsLevel[]; // Only available at these levels\n historicalLimit?: number; // Months of historical data available\n isEstimated?: boolean;\n isDeprecated?: boolean;\n actionType?: string; // For action-based metrics\n}\n\nexport interface BreakdownDefinition {\n key: string;\n name: string;\n description: string;\n category: BreakdownCategory;\n possibleValues?: string[];\n levelRestrictions?: InsightsLevel[]; // Only available at these levels\n historicalLimit?: number; // Months of historical data available\n blocksOffsiteMetrics?: boolean;\n requiresCombination?: string[]; // Must combine with these breakdowns\n cannotCombineWith?: string[]; // Cannot use with these breakdowns\n}\n\n// --- API Response Structures ---\n\nexport interface AdsActionStats {\n action_type: string;\n value: string; // Always string, parse to number\n \"1d_click\"?: string;\n \"7d_click\"?: string;\n \"28d_click\"?: string;\n \"1d_view\"?: string;\n \"1d_ev\"?: string;\n}\n\nexport interface AdsHistogramStats {\n action_type: string;\n value: number[];\n}\n\nexport interface ImageAsset {\n id: string;\n name?: string;\n url?: string;\n url_tags?: string;\n hash?: string;\n image_crops?: Record<string, unknown>;\n}\n\nexport interface VideoAsset {\n id: string;\n thumbnail_url?: string;\n video_id?: string;\n video_name?: string;\n}\n\nexport interface TextAsset {\n id: string;\n text: string;\n}\n\nexport interface InsightRow {\n // Identifiers\n account_id?: string;\n account_name?: string;\n account_currency?: string;\n campaign_id?: string;\n campaign_name?: string;\n adset_id?: string;\n adset_name?: string;\n ad_id?: string;\n ad_name?: string;\n\n // Time period\n date_start: string;\n date_stop: string;\n\n // Delivery metrics\n impressions?: string;\n reach?: string;\n frequency?: string;\n social_reach?: string;\n social_impressions?: string;\n full_view_impressions?: string;\n full_view_reach?: string;\n\n // Click metrics\n clicks?: string;\n unique_clicks?: string;\n inline_link_clicks?: string;\n unique_inline_link_clicks?: string;\n social_clicks?: string;\n unique_social_clicks?: string;\n instant_experience_clicks_to_open?: string;\n instant_experience_clicks_to_start?: string;\n\n // CTR metrics\n ctr?: string;\n unique_ctr?: string;\n inline_link_click_ctr?: string;\n unique_inline_link_click_ctr?: string;\n\n // Cost metrics\n spend?: string;\n cpc?: string;\n cpm?: string;\n cpp?: string;\n cost_per_inline_link_click?: string;\n cost_per_unique_click?: string;\n cost_per_unique_inline_link_click?: string;\n cost_per_inline_post_engagement?: string;\n cost_per_ad_click?: string;\n social_spend?: string;\n\n // Engagement metrics\n inline_post_engagement?: string;\n\n // Video metrics\n video_play_actions?: AdsActionStats[];\n video_10_sec_watched_actions?: AdsActionStats[];\n video_15_sec_watched_actions?: AdsActionStats[];\n video_30_sec_watched_actions?: AdsActionStats[];\n video_p25_watched_actions?: AdsActionStats[];\n video_p50_watched_actions?: AdsActionStats[];\n video_p75_watched_actions?: AdsActionStats[];\n video_p95_watched_actions?: AdsActionStats[];\n video_p100_watched_actions?: AdsActionStats[];\n video_thruplay_watched_actions?: AdsActionStats[];\n video_avg_time_watched_actions?: AdsActionStats[];\n video_time_watched_actions?: AdsActionStats[];\n video_continuous_2_sec_watched_actions?: AdsActionStats[];\n video_play_curve_actions?: AdsHistogramStats[];\n video_play_retention_0_to_15s_actions?: AdsHistogramStats[];\n video_play_retention_20_to_60s_actions?: AdsHistogramStats[];\n video_play_retention_graph_actions?: AdsHistogramStats[];\n\n // Canvas/Instant Experience metrics\n canvas_avg_view_percent?: string;\n canvas_avg_view_time?: string;\n canvas_component_avg_pct_view?: Record<string, unknown>;\n\n // Action metrics (arrays)\n actions?: AdsActionStats[];\n action_values?: AdsActionStats[];\n conversions?: AdsActionStats[];\n conversion_values?: AdsActionStats[];\n cost_per_action_type?: AdsActionStats[];\n cost_per_conversion?: AdsActionStats[];\n outbound_clicks?: AdsActionStats[];\n unique_outbound_clicks?: AdsActionStats[];\n outbound_clicks_ctr?: AdsActionStats[];\n unique_outbound_clicks_ctr?: AdsActionStats[];\n cost_per_outbound_click?: AdsActionStats[];\n cost_per_unique_outbound_click?: AdsActionStats[];\n website_ctr?: AdsActionStats[];\n cost_per_thruplay?: AdsActionStats[];\n instant_experience_outbound_clicks?: AdsActionStats[];\n\n // ROAS metrics\n purchase_roas?: AdsActionStats[];\n website_purchase_roas?: AdsActionStats[];\n mobile_app_purchase_roas?: AdsActionStats[];\n\n // Quality metrics\n quality_score_organic?: string;\n quality_score_ecpm?: string;\n quality_ranking?: string;\n engagement_rate_ranking?: string;\n conversion_rate_ranking?: string;\n relevance_score?: Record<string, unknown>;\n\n // Awareness metrics\n estimated_ad_recall_rate?: string;\n estimated_ad_recallers?: string;\n estimated_ad_recall_rate_lower_bound?: string;\n estimated_ad_recall_rate_upper_bound?: string;\n cost_per_estimated_ad_recallers?: string;\n\n // Store visits\n store_visit_actions?: AdsActionStats[];\n cost_per_store_visit_action?: AdsActionStats[];\n\n // Catalog metrics\n catalog_segment_actions?: AdsActionStats[];\n catalog_segment_value?: AdsActionStats[];\n\n // Results metrics\n results?: Array<{ indicator: string; values: Array<{ value: string }> }>;\n result_rate?: Array<{ indicator: string; values: Array<{ value: string }> }>;\n cost_per_result?: Array<{ indicator: string; values: Array<{ value: string }> }>;\n results_indicator?: string;\n\n // Campaign info\n objective?: string;\n optimization_goal?: string;\n bid_strategy?: string;\n budget_remaining?: string;\n buying_type?: string;\n labels?: string[];\n\n // Breakdowns (present if requested)\n age?: string;\n gender?: string;\n user_segment_key?: string;\n country?: string;\n region?: string;\n dma?: string;\n publisher_platform?: string;\n platform_position?: string;\n device_platform?: string;\n impression_device?: string;\n\n // Creative breakdowns\n image_asset?: ImageAsset;\n video_asset?: VideoAsset;\n body_asset?: TextAsset;\n title_asset?: TextAsset;\n description_asset?: TextAsset;\n call_to_action_asset?: TextAsset;\n link_url_asset?: { id: string; website_url: string };\n ad_format_asset?: string;\n media_type?: string;\n media_format?: string;\n\n // Product breakdowns\n product_id?: string;\n\n // Time breakdowns\n hourly_stats_aggregated_by_advertiser_time_zone?: string;\n hourly_stats_aggregated_by_audience_time_zone?: string;\n frequency_value?: string;\n\n // Action breakdowns\n action_carousel_card_id?: string;\n action_carousel_card_name?: string;\n\n // Allow additional fields\n [key: string]: unknown;\n}\n\nexport interface InsightsApiResponse {\n data: InsightRow[];\n paging?: {\n cursors: {\n before: string;\n after: string;\n };\n next?: string;\n previous?: string;\n };\n}\n\n// --- Insights Request Types ---\n\nexport interface InsightsQueryRequest {\n adAccountId: string;\n level: InsightsLevel;\n metrics: string[];\n breakdowns?: string[];\n actionBreakdowns?: string[];\n /** Additional dimension fields to include in results (e.g., effective_status, objective) */\n dimensions?: string[];\n timeRange?: TimeRange;\n datePreset?: DatePreset;\n timeIncrement?: TimeIncrement;\n filtering?: InsightFilter[];\n limit?: number;\n /** Attribution window settings for conversion metrics */\n attribution?: AttributionSettings;\n /** How to display attribution data (combined, click_only, view_only, split) */\n attributionDisplayMode?: AttributionDisplayMode;\n}\n\nexport interface InsightFilter {\n field: string;\n operator: \"EQUAL\" | \"NOT_EQUAL\" | \"GREATER_THAN\" | \"LESS_THAN\" | \"IN\" | \"NOT_IN\" | \"CONTAINS\";\n value: string | string[];\n}\n\n// --- Query Planning Types ---\n\nexport interface MetaApiRequest {\n type: \"delivery\" | \"actions\" | \"video\" | \"creative\";\n fields: string[];\n breakdowns?: string[];\n actionBreakdowns?: string[];\n level: InsightsLevel;\n timeRange?: TimeRange;\n datePreset?: DatePreset;\n timeIncrement?: TimeIncrement;\n limit?: number;\n attributionWindows?: string[];\n filtering?: Array<{ field: string; operator: string; value: string[] }>;\n}\n\nexport interface QueryPlan {\n requests: MetaApiRequest[];\n calculatedMetrics: string[];\n joinKeys: string[];\n warnings: string[];\n errors: string[];\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\n// --- Calculated Metrics Types ---\n\nexport interface DerivedMetrics {\n // Raw API metrics\n hookRate?: number;\n thumbstopRate?: number;\n holdRate?: number;\n completionRate?: number;\n clickToAtcRatio?: number;\n atcToPurchaseRatio?: number;\n clickToPurchaseRatio?: number;\n aov?: number;\n costPerAtc?: number;\n engagementRate?: number;\n\n // Share/Percentage metrics\n spendShare?: number;\n purchaseValueShare?: number;\n\n // CTR variants\n outboundCtr?: number;\n\n // ROAS variants\n subscriptionRoas?: number;\n leadsRoas?: number;\n\n // Per 1,000 impressions metrics\n purchasesPerMille?: number;\n revenuePerMille?: number;\n installsPerMille?: number;\n leadsPerMille?: number;\n\n // Engagement rate variants\n impressionsToLeadsRatio?: number;\n likeRate?: number;\n commentRate?: number;\n postShareRatio?: number;\n postEngagementsExclVideo?: number;\n postEngagementRateExclVideo?: number;\n\n // Video retention metrics\n video15sTo3sRetention?: number;\n thruplayCtr?: number;\n firstFrameRetention?: number;\n thumbstopClickRate?: number;\n costPerMinuteViewed?: number;\n\n // Video completion rate variants\n videoP25Rate?: number;\n videoP50Rate?: number;\n videoP75Rate?: number;\n videoP95Rate?: number;\n videoP100Rate?: number;\n\n // Video drop-off metrics\n dropOffP25ToP50?: number;\n dropOffP50ToP75?: number;\n dropOffP75ToP95?: number;\n dropOffP75ToP100?: number;\n dropOffP95ToP100?: number;\n sustainRate?: number;\n\n // Click quality & funnel metrics\n clickQuality?: number;\n averageLeadsValue?: number;\n\n // Click to conversion ratios\n clickToAppInstallRatio?: number;\n clickToLeadsRatio?: number;\n clickToSubscriptionsRatio?: number;\n clickToRegistrationsRatio?: number;\n clickToTrialStartedRatio?: number;\n leadsToSubscriptionsRatio?: number;\n clickToContactRatio?: number;\n clickToFindLocationRatio?: number;\n clickToAppointmentsRatio?: number;\n clickToInitiateCheckoutRatio?: number;\n clickToWebsitePurchaseRatio?: number;\n\n // App funnel metrics\n installToTrialRate?: number;\n installToPurchaseRatio?: number;\n installToRegistrationRate?: number;\n registrationToAchievementRate?: number;\n achievementToPurchaseRate?: number;\n\n // E-commerce funnel metrics\n viewContentRatio?: number;\n viewContentToAtcRatio?: number;\n checkoutToPurchaseRatio?: number;\n\n // AOV variants\n aovWebsite?: number;\n aovShop?: number;\n\n // Cost per action metrics (calculated as spend / count)\n cost_per_page_like?: number;\n cost_per_messaging_conversation?: number;\n cost_per_lead?: number;\n cost_per_purchase?: number;\n cost_per_add_to_cart?: number;\n cost_per_initiate_checkout?: number;\n cost_per_app_install?: number;\n cost_per_landing_page_view?: number;\n cost_per_3s_video_view?: number;\n}\n\n// --- OAuth Types ---\n\n\nexport interface MetaAdAccount {\n id: string;\n account_id: string;\n name: string;\n currency: string;\n timezone_name: string;\n account_status: number;\n}\n\n\n// --- Debug Types ---\n\nexport interface QueryDebugInfo {\n requestCount: number;\n rawRequests: MetaApiRequest[];\n calculatedMetrics: string[];\n joinKeys: string[];\n executionTimeMs: number;\n rowCount: number;\n warnings: string[];\n adAccountId?: string;\n}\n\nexport interface InsightsResponse {\n data: Record<string, unknown>[];\n debug: QueryDebugInfo;\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// DERIVED METRICS\n// Client-side metric calculations from raw API data\n// ============================================\n\nimport {\n InsightRow,\n AdsActionStats,\n DerivedMetrics,\n AttributionSettings,\n AttributionDisplayMode,\n ATTRIBUTION_PRESETS,\n} from \"./types.js\";\nimport { logger } from \"../../core/logger.js\";\n\n// ============================================\n// ACTION EXTRACTION UTILITIES\n// ============================================\n\n/**\n * Extract a specific action value from the actions array\n * @param actions - Array of AdsActionStats from API response\n * @param actionType - The action_type to find (e.g., \"offsite_conversion.fb_pixel_purchase\")\n * @returns The numeric value or 0 if not found\n */\n/**\n * Extract action value from actions array\n * @param actions - Array of AdsActionStats\n * @param actionTypes - Single action_type string or array of action_types to try in order\n * @returns The value of the first matching action, or 0 if not found\n */\nexport function getActionValue(\n actions: AdsActionStats[] | undefined,\n actionTypes: string | string[]\n): number {\n if (!actions || !Array.isArray(actions)) return 0;\n\n // Normalize to array\n const types = Array.isArray(actionTypes) ? actionTypes : [actionTypes];\n\n // Try each action type in order\n for (const actionType of types) {\n const action = actions.find((a) => a.action_type === actionType);\n if (action) {\n return parseFloat(action.value);\n }\n }\n\n return 0;\n}\n\n/**\n * Extract action value with specific attribution window\n * @param actions - Array of AdsActionStats\n * @param actionTypes - Single action_type or array of action_types to try in order\n * @param window - Attribution window: \"1d_click\", \"7d_click\", \"28d_click\", \"1d_view\", \"1d_ev\"\n */\nexport function getActionValueWithWindow(\n actions: AdsActionStats[] | undefined,\n actionTypes: string | string[],\n window: \"1d_click\" | \"7d_click\" | \"28d_click\" | \"1d_view\" | \"1d_ev\"\n): number {\n if (!actions || !Array.isArray(actions)) return 0;\n\n // Normalize to array\n const types = Array.isArray(actionTypes) ? actionTypes : [actionTypes];\n\n for (const actionType of types) {\n const action = actions.find((a) => a.action_type === actionType);\n if (action && action[window]) {\n return parseFloat(action[window]!);\n }\n }\n\n return 0;\n}\n\n/**\n * Get all action values for a specific action type\n */\nexport function getActionWithAllWindows(\n actions: AdsActionStats[] | undefined,\n actionTypes: string | string[]\n): {\n total: number;\n \"1d_click\": number;\n \"7d_click\": number;\n \"28d_click\": number;\n \"1d_view\": number;\n \"1d_ev\": number;\n} {\n const defaultValue = {\n total: 0,\n \"1d_click\": 0,\n \"7d_click\": 0,\n \"28d_click\": 0,\n \"1d_view\": 0,\n \"1d_ev\": 0,\n };\n\n if (!actions || !Array.isArray(actions)) return defaultValue;\n\n // Normalize to array\n const types = Array.isArray(actionTypes) ? actionTypes : [actionTypes];\n\n for (const actionType of types) {\n const action = actions.find((a) => a.action_type === actionType);\n if (action) {\n return {\n total: parseFloat(action.value) || 0,\n \"1d_click\": parseFloat(action[\"1d_click\"] || \"0\"),\n \"7d_click\": parseFloat(action[\"7d_click\"] || \"0\"),\n \"28d_click\": parseFloat(action[\"28d_click\"] || \"0\"),\n \"1d_view\": parseFloat(action[\"1d_view\"] || \"0\"),\n \"1d_ev\": parseFloat(action[\"1d_ev\"] || \"0\"),\n };\n }\n }\n\n return defaultValue;\n}\n\n/**\n * Get value from action_values array (for monetary values)\n */\nexport function getActionMonetaryValue(\n actionValues: AdsActionStats[] | undefined,\n actionTypes: string | string[]\n): number {\n return getActionValue(actionValues, actionTypes);\n}\n\n/**\n * Sum all values from an actions array\n */\nexport function sumAllActions(actions: AdsActionStats[] | undefined): number {\n if (!actions || !Array.isArray(actions)) return 0;\n return actions.reduce((sum, action) => sum + parseFloat(action.value), 0);\n}\n\n// ============================================\n// ATTRIBUTION WINDOW EXTRACTION\n// ============================================\n\n/**\n * Extract action value with custom attribution settings\n * Supports combining click + view attribution, or single window\n */\nexport function getActionValueWithAttribution(\n actions: AdsActionStats[] | undefined,\n actionTypes: string | string[],\n attribution: AttributionSettings\n): number {\n if (!actions || !Array.isArray(actions)) return 0;\n\n // Normalize to array\n const types = Array.isArray(actionTypes) ? actionTypes : [actionTypes];\n\n // Try each action type in order\n for (const actionType of types) {\n const action = actions.find((a) => a.action_type === actionType);\n if (action) {\n const clickValue = parseFloat(action[attribution.clickWindow] || \"0\");\n const viewValue = attribution.includeViewThrough\n ? parseFloat(action[attribution.viewWindow] || \"0\")\n : 0;\n return clickValue + viewValue;\n }\n }\n\n return 0;\n}\n\n/**\n * Extract action values split by click and view attribution\n * Returns separate values for click, view, and total\n */\nexport function getActionValuesSplit(\n actions: AdsActionStats[] | undefined,\n actionTypes: string | string[],\n attribution: AttributionSettings\n): { click: number; view: number; total: number } {\n if (!actions || !Array.isArray(actions)) {\n return { click: 0, view: 0, total: 0 };\n }\n\n // Normalize to array\n const types = Array.isArray(actionTypes) ? actionTypes : [actionTypes];\n\n // Try each action type in order\n for (const actionType of types) {\n const action = actions.find((a) => a.action_type === actionType);\n if (action) {\n const click = parseFloat(action[attribution.clickWindow] || \"0\");\n const view = parseFloat(action[attribution.viewWindow] || \"0\");\n\n return {\n click,\n view,\n total: click + view,\n };\n }\n }\n\n return { click: 0, view: 0, total: 0 };\n}\n\n/**\n * Get monetary value with custom attribution settings\n */\nexport function getActionMonetaryValueWithAttribution(\n actionValues: AdsActionStats[] | undefined,\n actionTypes: string | string[],\n attribution: AttributionSettings\n): number {\n return getActionValueWithAttribution(actionValues, actionTypes, attribution);\n}\n\n/**\n * Get monetary values split by attribution\n */\nexport function getActionMonetaryValuesSplit(\n actionValues: AdsActionStats[] | undefined,\n actionTypes: string | string[],\n attribution: AttributionSettings\n): { click: number; view: number; total: number } {\n return getActionValuesSplit(actionValues, actionTypes, attribution);\n}\n\n/**\n * Default attribution settings (7d click + 1d view)\n */\nexport const DEFAULT_ATTRIBUTION: AttributionSettings = ATTRIBUTION_PRESETS.default;\n\n// ============================================\n// VIDEO METRIC UTILITIES\n// ============================================\n\n/**\n * Extract video metric value from video metrics array\n * Video metrics like video_p25_watched_actions return AdsActionStats[] arrays\n * Format: [{ action_type: \"video_view\", value: \"1234\" }]\n *\n * This function handles multiple possible formats:\n * 1. Standard format with action_type=\"video_view\"\n * 2. Array with just the first item (no specific action_type)\n * 3. Empty array or undefined\n */\nexport function getVideoMetricValue(\n videoActions: AdsActionStats[] | undefined\n): number {\n if (!videoActions || !Array.isArray(videoActions) || videoActions.length === 0) {\n return 0;\n }\n\n // First, try to find the video_view action type (standard format)\n const videoAction = videoActions.find((a) => a.action_type === \"video_view\");\n if (videoAction && videoAction.value) {\n return parseFloat(videoAction.value) || 0;\n }\n\n // Fallback: if no video_view action_type, try the first item with a value\n // Some API responses may not include action_type for video metrics\n if (videoActions[0] && videoActions[0].value) {\n return parseFloat(videoActions[0].value) || 0;\n }\n\n return 0;\n}\n\n/**\n * Calculate video retention metrics\n */\nexport function calculateVideoRetention(row: InsightRow): {\n p25: number;\n p50: number;\n p75: number;\n p95: number;\n p100: number;\n avgWatchTime: number;\n retentionRate: number; // p100 / p25 * 100\n thruPlay: number;\n} {\n const p25 = getVideoMetricValue(row.video_p25_watched_actions);\n const p50 = getVideoMetricValue(row.video_p50_watched_actions);\n const p75 = getVideoMetricValue(row.video_p75_watched_actions);\n const p95 = getVideoMetricValue(row.video_p95_watched_actions);\n const p100 = getVideoMetricValue(row.video_p100_watched_actions);\n const avgWatchTime = getVideoMetricValue(row.video_avg_time_watched_actions);\n const thruPlay = getVideoMetricValue(row.video_thruplay_watched_actions);\n\n return {\n p25,\n p50,\n p75,\n p95,\n p100,\n avgWatchTime,\n retentionRate: p25 > 0 ? (p100 / p25) * 100 : 0,\n thruPlay,\n };\n}\n\n// ============================================\n// SAFE NUMBER PARSING\n// ============================================\n\n/**\n * Safely parse a numeric string\n */\nfunction safeParseFloat(value: string | undefined | null): number {\n if (!value) return 0;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? 0 : parsed;\n}\n\n/**\n * Safely parse an integer string\n */\nfunction safeParseInt(value: string | undefined | null): number {\n if (!value) return 0;\n const parsed = parseInt(value, 10);\n return isNaN(parsed) ? 0 : parsed;\n}\n\n// ============================================\n// DERIVED METRICS\n// ============================================\n\n/**\n * Calculate all derived metrics from a single row\n */\nexport function calculateDerivedMetrics(row: InsightRow): DerivedMetrics {\n // Extract base values\n const impressions = safeParseInt(row.impressions);\n const clicks = safeParseInt(row.clicks);\n const spend = safeParseFloat(row.spend);\n const linkClicks = getActionValue(row.actions, [\"link_click\"]);\n const videoViews3s = getActionValue(row.actions, [\"video_view\"]);\n\n // E-commerce funnel - Website (try omni_ first, then offsite_conversion)\n const addToCart = getActionValue(row.actions, [\"omni_add_to_cart\", \"offsite_conversion.fb_pixel_add_to_cart\"]);\n const purchases = getActionValue(row.actions, [\"omni_purchase\", \"offsite_conversion.fb_pixel_purchase\"]);\n const purchaseValue = getActionMonetaryValue(row.action_values, [\"omni_purchase\", \"offsite_conversion.fb_pixel_purchase\"]);\n const viewContent = getActionValue(row.actions, [\"omni_view_content\", \"offsite_conversion.fb_pixel_view_content\"]);\n const initiateCheckout = getActionValue(row.actions, [\"omni_initiated_checkout\", \"offsite_conversion.fb_pixel_initiate_checkout\"]);\n\n // Shop purchases (on-platform)\n const shopPurchases = getActionValue(row.actions, [\"onsite_conversion.purchase\"]);\n const shopPurchaseValue = getActionMonetaryValue(row.action_values, [\"onsite_conversion.purchase\"]);\n\n // Leads & subscriptions (try omni_ first)\n const leads = getActionValue(row.actions, [\"omni_lead\", \"offsite_conversion.fb_pixel_lead\"]);\n const leadsValue = getActionMonetaryValue(row.action_values, [\"omni_lead\", \"offsite_conversion.fb_pixel_lead\"]);\n const subscriptions = getActionValue(row.actions, [\"offsite_conversion.fb_pixel_subscribe\"]);\n const subscriptionValue = getActionMonetaryValue(row.action_values, [\"offsite_conversion.fb_pixel_subscribe\"]);\n const registrations = getActionValue(row.actions, [\"omni_complete_registration\", \"offsite_conversion.fb_pixel_complete_registration\"]);\n const contacts = getActionValue(row.actions, [\"offsite_conversion.fb_pixel_contact\"]);\n const findLocations = getActionValue(row.actions, [\"offsite_conversion.fb_pixel_find_location\"]);\n const appointments = getActionValue(row.actions, [\"offsite_conversion.fb_pixel_schedule\"]);\n const trials = getActionValue(row.actions, [\"offsite_conversion.fb_pixel_start_trial\"]);\n\n // App metrics\n const appInstalls = getActionValue(row.actions, [\"omni_app_install\", \"mobile_app_install\"]);\n const appPurchases = getActionValue(row.actions, [\"app_custom_event.fb_mobile_purchase\"]);\n const appTrials = getActionValue(row.actions, [\"app_custom_event.fb_mobile_start_trial\"]);\n const appRegistrations = getActionValue(row.actions, [\"app_custom_event.fb_mobile_complete_registration\"]);\n const achievements = getActionValue(row.actions, [\"app_custom_event.fb_mobile_achievement_unlocked\"]);\n\n // Outbound clicks - extract from array if needed\n let outboundClicks = 0;\n if (row.outbound_clicks && Array.isArray(row.outbound_clicks)) {\n const outboundAction = (row.outbound_clicks as AdsActionStats[]).find(\n (a) => a.action_type === \"outbound_click\"\n );\n outboundClicks = outboundAction ? parseFloat(outboundAction.value) : 0;\n }\n\n // Video retention\n const videoPlays = getVideoMetricValue(row.video_play_actions);\n const video_p25 = getVideoMetricValue(row.video_p25_watched_actions);\n const video_p50 = getVideoMetricValue(row.video_p50_watched_actions);\n const video_p75 = getVideoMetricValue(row.video_p75_watched_actions);\n const video_p95 = getVideoMetricValue(row.video_p95_watched_actions);\n const video_p100 = getVideoMetricValue(row.video_p100_watched_actions);\n const video15s = getVideoMetricValue(row.video_15_sec_watched_actions);\n const thruPlay = getVideoMetricValue(row.video_thruplay_watched_actions);\n\n // Total Watch Time: Use API value if available, otherwise calculate fallback\n // Fallback: Total Watch Time ≈ Average Watch Time × Video Views (3s)\n let totalWatchTime = getVideoMetricValue(row.video_time_watched_actions);\n if (totalWatchTime === 0) {\n const avgWatchTime = getVideoMetricValue(row.video_avg_time_watched_actions);\n if (avgWatchTime > 0 && videoViews3s > 0) {\n totalWatchTime = avgWatchTime * videoViews3s;\n }\n }\n\n // Additional funnel metrics needed for cost_per calculations\n const landingPageViews = getActionValue(row.actions, [\"landing_page_view\"]);\n const messagingConversations = getActionValue(row.actions, [\"onsite_conversion.messaging_conversation_started_7d\"]);\n\n // Engagement\n const engagement = safeParseInt(row.inline_post_engagement);\n const pageLikes = getActionValue(row.actions, \"like\"); // PAGE likes (following the page)\n const postReactions = getActionValue(row.actions, \"post_reaction\"); // POST reactions (likes, love, etc. on the ad)\n const comments = getActionValue(row.actions, \"comment\");\n const shares = getActionValue(row.actions, \"post\");\n\n return {\n // ===== ORIGINAL BENLY METRICS =====\n // Video creative metrics\n hookRate: impressions > 0 ? (videoViews3s / impressions) * 100 : undefined,\n thumbstopRate: impressions > 0 ? (videoPlays / impressions) * 100 : undefined,\n holdRate: video_p25 > 0 ? (video_p50 / video_p25) * 100 : undefined,\n completionRate: video_p25 > 0 ? (video_p100 / video_p25) * 100 : undefined,\n\n // E-commerce funnel metrics\n clickToAtcRatio: linkClicks > 0 ? (addToCart / linkClicks) * 100 : undefined,\n atcToPurchaseRatio: addToCart > 0 ? (purchases / addToCart) * 100 : undefined,\n clickToPurchaseRatio: linkClicks > 0 ? (purchases / linkClicks) * 100 : undefined,\n aov: purchases > 0 ? purchaseValue / purchases : undefined,\n costPerAtc: addToCart > 0 ? spend / addToCart : undefined,\n\n // ===== COST PER METRICS (calculated as spend / count) =====\n // These are calculated client-side for reliability since Meta's cost_per_action_type\n // array may not always contain all action types\n cost_per_page_like: pageLikes > 0 ? spend / pageLikes : undefined,\n cost_per_messaging_conversation: messagingConversations > 0 ? spend / messagingConversations : undefined,\n cost_per_lead: leads > 0 ? spend / leads : undefined,\n cost_per_purchase: purchases > 0 ? spend / purchases : undefined,\n cost_per_add_to_cart: addToCart > 0 ? spend / addToCart : undefined,\n cost_per_initiate_checkout: initiateCheckout > 0 ? spend / initiateCheckout : undefined,\n cost_per_app_install: appInstalls > 0 ? spend / appInstalls : undefined,\n cost_per_landing_page_view: landingPageViews > 0 ? spend / landingPageViews : undefined,\n cost_per_3s_video_view: videoViews3s > 0 ? spend / videoViews3s : undefined,\n\n // Engagement\n engagementRate: impressions > 0 ? (engagement / impressions) * 100 : undefined,\n\n // ===== NEW METRICS FROM COMPETITOR ANALYSIS =====\n\n // Share/Percentage metrics (Note: requires total context - placeholder calculation)\n // These metrics need access to the total dataset to calculate shares\n // For single row, we return undefined and handle in aggregate functions\n spendShare: undefined, // Calculated at aggregate level\n purchaseValueShare: undefined, // Calculated at aggregate level\n\n // CTR variants\n outboundCtr: impressions > 0 ? (outboundClicks / impressions) * 100 : undefined,\n\n // ROAS variants\n subscriptionRoas: spend > 0 && subscriptionValue > 0 ? subscriptionValue / spend : undefined,\n leadsRoas: spend > 0 && leadsValue > 0 ? leadsValue / spend : undefined,\n\n // Per 1,000 impressions metrics\n purchasesPerMille: impressions > 0 ? (purchases / impressions) * 1000 : undefined,\n revenuePerMille: impressions > 0 ? (purchaseValue / impressions) * 1000 : undefined,\n installsPerMille: impressions > 0 ? (appInstalls / impressions) * 1000 : undefined,\n leadsPerMille: impressions > 0 ? (leads / impressions) * 1000 : undefined,\n\n // Engagement rate variants\n impressionsToLeadsRatio: impressions > 0 ? (leads / impressions) * 100 : undefined,\n likeRate: impressions > 0 ? (postReactions / impressions) * 100 : undefined, // Uses POST reactions, not page likes\n commentRate: impressions > 0 ? (comments / impressions) * 100 : undefined,\n postShareRatio: impressions > 0 ? (shares / impressions) * 100 : undefined,\n postEngagementsExclVideo: engagement > 0 && videoViews3s > 0\n ? Math.max(0, engagement - videoViews3s)\n : engagement,\n postEngagementRateExclVideo: impressions > 0\n ? (Math.max(0, engagement - videoViews3s) / impressions) * 100\n : undefined,\n\n // Video retention metrics\n video15sTo3sRetention: videoViews3s > 0 ? (video15s / videoViews3s) * 100 : undefined,\n // ThruPlay CTR = percentage of impressions that resulted in a ThruPlay\n // NOT linkClicks/thruPlay which can exceed 100%!\n thruplayCtr: impressions > 0 ? (thruPlay / impressions) * 100 : undefined,\n firstFrameRetention: impressions > 0 ? (videoPlays / impressions) * 100 : undefined, // Same as thumbstopRate\n thumbstopClickRate: videoPlays > 0 ? (linkClicks / videoPlays) * 100 : undefined,\n costPerMinuteViewed: totalWatchTime > 0 ? spend / (totalWatchTime / 60) : undefined,\n\n // Video completion rate variants (per impression)\n videoP25Rate: impressions > 0 ? (video_p25 / impressions) * 100 : undefined,\n videoP50Rate: impressions > 0 ? (video_p50 / impressions) * 100 : undefined,\n videoP75Rate: impressions > 0 ? (video_p75 / impressions) * 100 : undefined,\n videoP95Rate: impressions > 0 ? (video_p95 / impressions) * 100 : undefined,\n videoP100Rate: impressions > 0 ? (video_p100 / impressions) * 100 : undefined,\n\n // Video drop-off metrics\n dropOffP25ToP50: video_p25 > 0 ? (1 - video_p50 / video_p25) * 100 : undefined,\n dropOffP50ToP75: video_p50 > 0 ? (1 - video_p75 / video_p50) * 100 : undefined,\n dropOffP75ToP95: video_p75 > 0 ? (1 - video_p95 / video_p75) * 100 : undefined,\n dropOffP75ToP100: video_p75 > 0 ? (1 - video_p100 / video_p75) * 100 : undefined,\n dropOffP95ToP100: video_p95 > 0 ? (1 - video_p100 / video_p95) * 100 : undefined,\n sustainRate: impressions > 0 ? (thruPlay / impressions) * 100 : undefined,\n\n // Click quality & funnel metrics\n clickQuality: clicks > 0 ? (purchases / clicks) * 100 : undefined,\n averageLeadsValue: leads > 0 ? leadsValue / leads : undefined,\n\n // Click to conversion ratios\n clickToAppInstallRatio: linkClicks > 0 ? (appInstalls / linkClicks) * 100 : undefined,\n clickToLeadsRatio: linkClicks > 0 ? (leads / linkClicks) * 100 : undefined,\n clickToSubscriptionsRatio: linkClicks > 0 ? (subscriptions / linkClicks) * 100 : undefined,\n clickToRegistrationsRatio: linkClicks > 0 ? (registrations / linkClicks) * 100 : undefined,\n clickToTrialStartedRatio: linkClicks > 0 ? (trials / linkClicks) * 100 : undefined,\n leadsToSubscriptionsRatio: leads > 0 ? (subscriptions / leads) * 100 : undefined,\n clickToContactRatio: linkClicks > 0 ? (contacts / linkClicks) * 100 : undefined,\n clickToFindLocationRatio: linkClicks > 0 ? (findLocations / linkClicks) * 100 : undefined,\n clickToAppointmentsRatio: linkClicks > 0 ? (appointments / linkClicks) * 100 : undefined,\n clickToInitiateCheckoutRatio: linkClicks > 0 ? (initiateCheckout / linkClicks) * 100 : undefined,\n clickToWebsitePurchaseRatio: linkClicks > 0 ? (purchases / linkClicks) * 100 : undefined,\n\n // App funnel metrics\n installToTrialRate: appInstalls > 0 ? (appTrials / appInstalls) * 100 : undefined,\n installToPurchaseRatio: appInstalls > 0 ? (appPurchases / appInstalls) * 100 : undefined,\n installToRegistrationRate: appInstalls > 0 ? (appRegistrations / appInstalls) * 100 : undefined,\n registrationToAchievementRate: appRegistrations > 0 ? (achievements / appRegistrations) * 100 : undefined,\n achievementToPurchaseRate: achievements > 0 ? (appPurchases / achievements) * 100 : undefined,\n\n // E-commerce funnel metrics\n viewContentRatio: impressions > 0 ? (viewContent / impressions) * 100 : undefined,\n viewContentToAtcRatio: viewContent > 0 ? (addToCart / viewContent) * 100 : undefined,\n checkoutToPurchaseRatio: initiateCheckout > 0 ? (purchases / initiateCheckout) * 100 : undefined,\n\n // AOV variants\n aovWebsite: purchases > 0 ? purchaseValue / purchases : undefined, // Same as aov for website\n aovShop: shopPurchases > 0 ? shopPurchaseValue / shopPurchases : undefined,\n };\n}\n\n/**\n * Calculate a single derived metric\n */\nexport function calculateSingleMetric(\n row: InsightRow,\n metricKey: string\n): number | undefined {\n const metrics = calculateDerivedMetrics(row);\n return metrics[metricKey as keyof DerivedMetrics];\n}\n\n/**\n * Enrich data with calculated metrics\n */\nexport function enrichWithCalculatedMetrics(\n data: InsightRow[],\n calculatedMetricKeys: string[]\n): (InsightRow & Partial<DerivedMetrics>)[] {\n if (calculatedMetricKeys.length === 0) {\n return data;\n }\n\n return data.map((row) => {\n const calculated = calculateDerivedMetrics(row);\n const enriched: InsightRow & Partial<DerivedMetrics> = { ...row };\n\n for (const key of calculatedMetricKeys) {\n const value = calculated[key as keyof DerivedMetrics];\n if (value !== undefined) {\n (enriched as Record<string, unknown>)[key] = value;\n }\n }\n\n return enriched;\n });\n}\n\n/**\n * Calculate share metrics (spendShare, purchaseValueShare) for a dataset\n * These metrics require the total dataset to calculate percentages\n * @param data - Array of InsightRow data\n * @returns Data enriched with share metrics\n */\nexport function calculateShareMetrics(\n data: (InsightRow & Partial<DerivedMetrics>)[]\n): (InsightRow & Partial<DerivedMetrics>)[] {\n if (data.length === 0) return data;\n\n // Calculate totals\n let totalSpend = 0;\n let totalPurchaseValue = 0;\n\n for (const row of data) {\n totalSpend += parseFloat(row.spend || \"0\");\n totalPurchaseValue += getActionMonetaryValue(\n row.action_values,\n \"offsite_conversion.fb_pixel_purchase\"\n );\n }\n\n // Calculate share for each row\n return data.map((row) => {\n const rowSpend = parseFloat(row.spend || \"0\");\n const rowPurchaseValue = getActionMonetaryValue(\n row.action_values,\n \"offsite_conversion.fb_pixel_purchase\"\n );\n\n return {\n ...row,\n spendShare: totalSpend > 0 ? (rowSpend / totalSpend) * 100 : undefined,\n purchaseValueShare: totalPurchaseValue > 0\n ? (rowPurchaseValue / totalPurchaseValue) * 100\n : undefined,\n };\n });\n}\n\n/**\n * Full enrichment with all calculated metrics including share metrics\n * @param data - Array of InsightRow data\n * @param calculatedMetricKeys - List of calculated metric keys to include\n * @param includeShareMetrics - Whether to calculate share metrics (requires full dataset)\n * @returns Fully enriched data\n */\nexport function enrichWithAllCalculatedMetrics(\n data: InsightRow[],\n calculatedMetricKeys: string[],\n includeShareMetrics: boolean = true\n): (InsightRow & Partial<DerivedMetrics>)[] {\n // First apply basic calculated metrics\n let enriched = enrichWithCalculatedMetrics(data, calculatedMetricKeys);\n\n // Then apply share metrics if requested\n if (includeShareMetrics && (calculatedMetricKeys.includes(\"spendShare\") || calculatedMetricKeys.includes(\"purchaseValueShare\"))) {\n enriched = calculateShareMetrics(enriched);\n }\n\n return enriched;\n}\n\n// ============================================\n// E-COMMERCE FUNNEL ANALYSIS\n// ============================================\n\n/**\n * Calculate complete e-commerce funnel from row data\n */\nexport function calculateEcommerceFunnel(row: InsightRow): {\n impressions: number;\n clicks: number;\n landingPageViews: number;\n contentViews: number;\n addToCart: number;\n initiateCheckout: number;\n purchases: number;\n revenue: number;\n // Conversion rates\n ctr: number;\n landingPageRate: number;\n viewToCartRate: number;\n cartToCheckoutRate: number;\n checkoutToPurchaseRate: number;\n overallConversionRate: number;\n // Costs\n cpc: number;\n costPerLanding: number;\n costPerView: number;\n costPerAtc: number;\n costPerCheckout: number;\n costPerPurchase: number;\n // ROAS\n roas: number;\n aov: number;\n} {\n const impressions = safeParseInt(row.impressions);\n const clicks = safeParseInt(row.clicks);\n const spend = safeParseFloat(row.spend);\n\n const landingPageViews = getActionValue(row.actions, \"landing_page_view\");\n const contentViews = getActionValue(\n row.actions,\n \"offsite_conversion.fb_pixel_view_content\"\n );\n const addToCart = getActionValue(\n row.actions,\n \"offsite_conversion.fb_pixel_add_to_cart\"\n );\n const initiateCheckout = getActionValue(\n row.actions,\n \"offsite_conversion.fb_pixel_initiate_checkout\"\n );\n const purchases = getActionValue(\n row.actions,\n \"offsite_conversion.fb_pixel_purchase\"\n );\n const revenue = getActionMonetaryValue(\n row.action_values,\n \"offsite_conversion.fb_pixel_purchase\"\n );\n\n return {\n // Absolute numbers\n impressions,\n clicks,\n landingPageViews,\n contentViews,\n addToCart,\n initiateCheckout,\n purchases,\n revenue,\n\n // Conversion rates\n ctr: impressions > 0 ? (clicks / impressions) * 100 : 0,\n landingPageRate: clicks > 0 ? (landingPageViews / clicks) * 100 : 0,\n viewToCartRate: contentViews > 0 ? (addToCart / contentViews) * 100 : 0,\n cartToCheckoutRate: addToCart > 0 ? (initiateCheckout / addToCart) * 100 : 0,\n checkoutToPurchaseRate:\n initiateCheckout > 0 ? (purchases / initiateCheckout) * 100 : 0,\n overallConversionRate: clicks > 0 ? (purchases / clicks) * 100 : 0,\n\n // Cost metrics\n cpc: clicks > 0 ? spend / clicks : 0,\n costPerLanding: landingPageViews > 0 ? spend / landingPageViews : 0,\n costPerView: contentViews > 0 ? spend / contentViews : 0,\n costPerAtc: addToCart > 0 ? spend / addToCart : 0,\n costPerCheckout: initiateCheckout > 0 ? spend / initiateCheckout : 0,\n costPerPurchase: purchases > 0 ? spend / purchases : 0,\n\n // ROI metrics\n roas: spend > 0 ? revenue / spend : 0,\n aov: purchases > 0 ? revenue / purchases : 0,\n };\n}\n\n// ============================================\n// CREATIVE PERFORMANCE ANALYSIS\n// ============================================\n\n/**\n * Calculate creative performance metrics\n */\nexport function calculateCreativePerformance(row: InsightRow): {\n // Basic metrics\n impressions: number;\n reach: number;\n frequency: number;\n clicks: number;\n ctr: number;\n spend: number;\n cpc: number;\n cpm: number;\n\n // Video metrics (if applicable)\n hasVideo: boolean;\n videoViews: number;\n hookRate: number;\n thumbstopRate: number;\n holdRate: number;\n completionRate: number;\n avgWatchTime: number;\n costPerThruPlay: number;\n\n // Engagement\n engagements: number;\n engagementRate: number;\n costPerEngagement: number;\n\n // Conversions\n purchases: number;\n purchaseValue: number;\n roas: number;\n costPerPurchase: number;\n\n // Creative info\n adFormat: string | undefined;\n} {\n const impressions = safeParseInt(row.impressions);\n const reach = safeParseInt(row.reach);\n const clicks = safeParseInt(row.clicks);\n const spend = safeParseFloat(row.spend);\n const engagements = safeParseInt(row.inline_post_engagement);\n\n // Video metrics\n const videoViews = getActionValue(row.actions, \"video_view\");\n const videoPlays = getVideoMetricValue(row.video_play_actions);\n const video_p25 = getVideoMetricValue(row.video_p25_watched_actions);\n const video_p50 = getVideoMetricValue(row.video_p50_watched_actions);\n const video_p100 = getVideoMetricValue(row.video_p100_watched_actions);\n const avgWatchTime = getVideoMetricValue(row.video_avg_time_watched_actions);\n const thruPlay = getVideoMetricValue(row.video_thruplay_watched_actions);\n\n const hasVideo = video_p25 > 0 || videoPlays > 0;\n\n // Conversions\n const purchases = getActionValue(\n row.actions,\n \"offsite_conversion.fb_pixel_purchase\"\n );\n const purchaseValue = getActionMonetaryValue(\n row.action_values,\n \"offsite_conversion.fb_pixel_purchase\"\n );\n\n // Cost per thruplay from API or calculate\n let costPerThruPlay = 0;\n if (row.cost_per_thruplay && row.cost_per_thruplay.length > 0) {\n costPerThruPlay = parseFloat(row.cost_per_thruplay[0].value) || 0;\n } else if (thruPlay > 0) {\n costPerThruPlay = spend / thruPlay;\n }\n\n return {\n impressions,\n reach,\n frequency: reach > 0 ? impressions / reach : 0,\n clicks,\n ctr: impressions > 0 ? (clicks / impressions) * 100 : 0,\n spend,\n cpc: clicks > 0 ? spend / clicks : 0,\n cpm: impressions > 0 ? (spend / impressions) * 1000 : 0,\n\n hasVideo,\n videoViews,\n hookRate: impressions > 0 ? (videoViews / impressions) * 100 : 0,\n thumbstopRate: impressions > 0 ? (videoPlays / impressions) * 100 : 0,\n holdRate: video_p25 > 0 ? (video_p50 / video_p25) * 100 : 0,\n completionRate: video_p25 > 0 ? (video_p100 / video_p25) * 100 : 0,\n avgWatchTime,\n costPerThruPlay,\n\n engagements,\n engagementRate: impressions > 0 ? (engagements / impressions) * 100 : 0,\n costPerEngagement: engagements > 0 ? spend / engagements : 0,\n\n purchases,\n purchaseValue,\n roas: spend > 0 ? purchaseValue / spend : 0,\n costPerPurchase: purchases > 0 ? spend / purchases : 0,\n\n adFormat: row.ad_format_asset,\n };\n}\n\n// ============================================\n// ACTION STATS NORMALIZATION FOR DISPLAY\n// ============================================\n\n/**\n * Fields that return AdsActionStats[] and need to be normalized to numeric values\n */\nconst ACTION_STATS_FIELDS = [\n \"purchase_roas\",\n \"website_purchase_roas\",\n \"mobile_app_purchase_roas\",\n \"cost_per_thruplay\",\n \"outbound_clicks\",\n \"unique_outbound_clicks\",\n \"outbound_clicks_ctr\",\n \"unique_outbound_clicks_ctr\",\n \"cost_per_outbound_click\",\n \"cost_per_unique_outbound_click\",\n \"cost_per_action_type\",\n \"cost_per_conversion\",\n \"website_ctr\",\n];\n\n/**\n * Video metrics that return AdsActionStats[] and need to be normalized\n * These contain action_type=\"video_view\" with the numeric value\n */\nconst VIDEO_METRICS_FIELDS = [\n \"video_p25_watched_actions\",\n \"video_p50_watched_actions\",\n \"video_p75_watched_actions\",\n \"video_p95_watched_actions\",\n \"video_p100_watched_actions\",\n \"video_avg_time_watched_actions\",\n \"video_thruplay_watched_actions\",\n \"video_play_actions\",\n \"video_30_sec_watched_actions\",\n \"video_10_sec_watched_actions\",\n \"video_15_sec_watched_actions\",\n \"video_time_watched_actions\", // Total Watch Time - was missing!\n];\n\n/**\n * Engagement metrics that may need normalization\n */\nconst ENGAGEMENT_ACTION_TYPES: Record<string, string> = {\n post_engagement: \"post_engagement\",\n page_engagement: \"page_engagement\",\n post_reactions: \"post_reaction\",\n comments: \"comment\",\n shares: \"post\",\n saves: \"onsite_conversion.post_save\",\n page_likes: \"like\",\n photo_views: \"photo_view\",\n video_views: \"video_view\",\n};\n\n/**\n * Map of metric keys to the action_type to extract from the actions array (COUNTS only)\n * NOTE: Monetary values (purchase_value, omni_purchase_value) come from action_values array, not here!\n * IMPORTANT: This mapping must include ALL metrics with apiField: \"actions\" from metricCatalog.ts\n */\n/**\n * ACTIONS_COUNT_METRICS maps metric keys to an array of possible action_types.\n * Meta API returns different action_type prefixes depending on the campaign setup:\n * - \"omni_*\" for aggregated conversions (default for most campaigns)\n * - \"offsite_conversion.fb_pixel_*\" for web-specific pixel events\n *\n * We try each action type in order and use the first match found.\n */\nconst ACTIONS_COUNT_METRICS: Record<string, string[]> = {\n // E-commerce funnel - try omni_ first (most common), then offsite_conversion\n purchases: [\"omni_purchase\", \"offsite_conversion.fb_pixel_purchase\"],\n omni_purchases: [\"omni_purchase\"],\n add_to_cart: [\"omni_add_to_cart\", \"offsite_conversion.fb_pixel_add_to_cart\"],\n initiate_checkout: [\"omni_initiated_checkout\", \"offsite_conversion.fb_pixel_initiate_checkout\"],\n view_content: [\"omni_view_content\", \"offsite_conversion.fb_pixel_view_content\"],\n add_payment_info: [\"omni_add_payment_info\", \"offsite_conversion.fb_pixel_add_payment_info\"],\n search: [\"omni_search\", \"offsite_conversion.fb_pixel_search\"],\n add_to_wishlist: [\"omni_add_to_wishlist\", \"offsite_conversion.fb_pixel_add_to_wishlist\"],\n\n // Leads & conversions\n leads: [\"omni_lead\", \"offsite_conversion.fb_pixel_lead\"],\n onsite_leads: [\"onsite_conversion.lead_grouped\"],\n leadgen_grouped: [\"leadgen_grouped\"],\n complete_registration: [\"omni_complete_registration\", \"offsite_conversion.fb_pixel_complete_registration\"],\n contact: [\"offsite_conversion.fb_pixel_contact\"],\n schedule: [\"offsite_conversion.fb_pixel_schedule\"],\n start_trial: [\"offsite_conversion.fb_pixel_start_trial\"],\n subscribe: [\"offsite_conversion.fb_pixel_subscribe\"],\n find_location: [\"offsite_conversion.fb_pixel_find_location\"],\n\n // Messaging\n messaging_conversations_started: [\"onsite_conversion.messaging_conversation_started_7d\"],\n messaging_first_reply: [\"onsite_conversion.messaging_first_reply\"],\n\n // Website-specific (pixel only, excludes app and shop)\n website_purchases: [\"offsite_conversion.fb_pixel_purchase\"],\n\n // App metrics\n app_installs: [\"omni_app_install\", \"mobile_app_install\"],\n app_add_to_cart: [\"app_custom_event.fb_mobile_add_to_cart\"],\n app_initiate_checkout: [\"app_custom_event.fb_mobile_initiated_checkout\"],\n app_complete_registration: [\"app_custom_event.fb_mobile_complete_registration\"],\n achievement_unlocked: [\"app_custom_event.fb_mobile_achievement_unlocked\"],\n credit_spent: [\"app_custom_event.fb_mobile_spent_credits\"],\n app_start_trial: [\"app_custom_event.fb_mobile_start_trial\"],\n app_subscribe: [\"app_custom_event.fb_mobile_subscribe\"],\n app_purchases: [\"app_custom_event.fb_mobile_purchase\"],\n\n // Shop / On-platform\n shop_purchases: [\"onsite_conversion.purchase\"],\n\n // Engagement & clicks\n landing_page_views: [\"landing_page_view\"],\n link_clicks: [\"link_click\"],\n\n // Video views (from actions array)\n video_views_3s: [\"video_view\"],\n\n // Social engagement metrics\n post_reactions: [\"post_reaction\"], // Reactions on posts (like, love, haha, wow, sad, angry)\n comments: [\"comment\"],\n shares: [\"post\"], // Shares = posting to timeline\n page_likes: [\"like\"], // Page likes/follows\n};\n\n/**\n * Metrics that should NOT have attribution windows applied.\n * These are engagement metrics, not conversion events.\n * Attribution should only be applied to purchase/conversion funnel metrics.\n */\nconst NON_ATTRIBUTION_METRICS = new Set([\n \"video_views_3s\", // Video views are engagement, not conversion\n \"landing_page_views\", // Landing page views are engagement, not conversion\n \"link_clicks\", // Link clicks are engagement, not conversion\n \"post_reactions\", // Social engagement\n \"comments\", // Social engagement\n \"shares\", // Social engagement\n \"page_likes\", // Social engagement\n]);\n\n/**\n * ACTION_VALUES_METRICS maps metric keys to an array of possible action_types.\n * Similar to ACTIONS_COUNT_METRICS, Meta API returns different prefixes.\n * IMPORTANT: This mapping must include ALL metrics with apiField: \"action_values\" from metricCatalog.ts\n */\nconst ACTION_VALUES_METRICS: Record<string, string[]> = {\n // E-commerce funnel values - try omni_ first, then offsite_conversion\n purchase_value: [\"omni_purchase\", \"offsite_conversion.fb_pixel_purchase\"],\n omni_purchase_value: [\"omni_purchase\"],\n add_to_cart_value: [\"omni_add_to_cart\", \"offsite_conversion.fb_pixel_add_to_cart\"],\n initiate_checkout_value: [\"omni_initiated_checkout\", \"offsite_conversion.fb_pixel_initiate_checkout\"],\n view_content_value: [\"omni_view_content\", \"offsite_conversion.fb_pixel_view_content\"],\n\n // Lead & conversion values\n subscribe_value: [\"offsite_conversion.fb_pixel_subscribe\"],\n leads_value: [\"omni_lead\", \"offsite_conversion.fb_pixel_lead\"],\n start_trial_value: [\"offsite_conversion.fb_pixel_start_trial\"],\n\n // Website-specific values (pixel only, excludes app and shop)\n website_purchase_value: [\"offsite_conversion.fb_pixel_purchase\"],\n\n // App-specific values\n app_purchase_value: [\"app_custom_event.fb_mobile_purchase\"],\n\n // Shop / On-platform values\n shop_purchase_value: [\"onsite_conversion.purchase\"],\n};\n\n/**\n * Map of metric keys to the action_type to extract from the cost_per_action_type array\n * IMPORTANT: This mapping must include ALL metrics with apiField: \"cost_per_action_type\" from metricCatalog.ts\n *\n * Bug #9 Fix: Without this mapping, all cost_per_* metrics would show the same value (value[0] of the array)\n */\n/**\n * COST_PER_ACTION_METRICS maps metric keys to an array of possible action_types.\n * Meta API returns different action_type prefixes depending on the campaign setup:\n * - \"omni_*\" for aggregated conversions (default for most campaigns)\n * - \"offsite_conversion.fb_pixel_*\" for web-specific pixel events\n * - \"app_custom_event.*\" for app-specific events\n *\n * We try each action type in order and use the first match found.\n */\nconst COST_PER_ACTION_METRICS: Record<string, string[]> = {\n // Conversions - try omni_ first (most common), then offsite_conversion\n cost_per_purchase: [\"omni_purchase\", \"offsite_conversion.fb_pixel_purchase\"],\n cost_per_lead: [\"omni_lead\", \"offsite_conversion.fb_pixel_lead\"],\n cost_per_add_to_cart: [\"omni_add_to_cart\", \"offsite_conversion.fb_pixel_add_to_cart\"],\n cost_per_initiate_checkout: [\"omni_initiated_checkout\", \"offsite_conversion.fb_pixel_initiate_checkout\"],\n cost_per_complete_registration: [\"omni_complete_registration\", \"offsite_conversion.fb_pixel_complete_registration\"],\n cost_per_contact: [\"omni_custom\", \"offsite_conversion.fb_pixel_contact\"],\n cost_per_schedule: [\"omni_custom\", \"offsite_conversion.fb_pixel_schedule\"],\n cost_per_start_trial: [\"omni_custom\", \"offsite_conversion.fb_pixel_start_trial\"],\n cost_per_subscribe: [\"omni_custom\", \"offsite_conversion.fb_pixel_subscribe\"],\n cost_per_view_content: [\"omni_view_content\", \"offsite_conversion.fb_pixel_view_content\"],\n cost_per_search: [\"omni_search\", \"offsite_conversion.fb_pixel_search\"],\n cost_per_add_to_wishlist: [\"omni_add_to_wishlist\", \"offsite_conversion.fb_pixel_add_to_wishlist\"],\n cost_per_add_payment_info: [\"omni_add_payment_info\", \"offsite_conversion.fb_pixel_add_payment_info\"],\n cost_per_find_location: [\"offsite_conversion.fb_pixel_find_location\"],\n\n // App events\n cost_per_app_install: [\"omni_app_install\", \"mobile_app_install\"],\n cost_per_app_add_to_cart: [\"app_custom_event.fb_mobile_add_to_cart\"],\n cost_per_app_initiate_checkout: [\"app_custom_event.fb_mobile_initiated_checkout\"],\n cost_per_app_purchase: [\"app_custom_event.fb_mobile_purchase\"],\n\n // Engagement\n cost_per_page_like: [\"like\"],\n cost_per_page_engagement: [\"page_engagement\"],\n cost_per_post_engagement: [\"post_engagement\"],\n cost_per_link_click: [\"link_click\"],\n cost_per_landing_page_view: [\"landing_page_view\"],\n cost_per_3s_video_view: [\"video_view\"],\n\n // Messaging\n cost_per_messaging_conversation: [\"onsite_conversion.messaging_conversation_started_7d\"],\n cost_per_messaging_first_reply: [\"onsite_conversion.messaging_first_reply\"],\n\n // On-platform\n cost_per_onsite_lead: [\"onsite_conversion.lead_grouped\"],\n cost_per_shop_purchase: [\"onsite_conversion.purchase\"],\n};\n\n/**\n * Extract a numeric value from an AdsActionStats array\n * - If actionType is provided, find the matching action_type\n * - Otherwise, return the first value or sum all values\n */\nfunction extractValueFromActionStats(\n actionStats: AdsActionStats[] | undefined,\n actionType?: string\n): number | undefined {\n if (!actionStats || !Array.isArray(actionStats) || actionStats.length === 0) {\n return undefined;\n }\n\n if (actionType) {\n // Find specific action type\n const action = actionStats.find((a) => a.action_type === actionType);\n if (action) {\n return parseFloat(action.value);\n }\n return undefined;\n }\n\n // For fields like purchase_roas that have a single value or \"omni_purchase\" type\n // Try common action types first\n const commonTypes = [\"omni_purchase\", \"offsite_conversion.fb_pixel_purchase\"];\n for (const type of commonTypes) {\n const action = actionStats.find((a) => a.action_type === type);\n if (action) {\n return parseFloat(action.value);\n }\n }\n\n // Fall back to first value\n if (actionStats.length > 0) {\n return parseFloat(actionStats[0].value);\n }\n\n return undefined;\n}\n\n/**\n * Normalize a single insight row by extracting numeric values from AdsActionStats[] arrays\n * This makes the data display-ready for the frontend\n */\nexport function normalizeRowForDisplay(row: InsightRow): InsightRow & Record<string, unknown> {\n const normalized: InsightRow & Record<string, unknown> = { ...row };\n\n // Normalize action stats fields (purchase_roas, cost_per_thruplay, etc.)\n // Debug: Log which ACTION_STATS_FIELDS are present in the row\n if (!(\"_actionStatsLogged\" in normalizeRowForDisplay)) {\n (normalizeRowForDisplay as unknown as Record<string, boolean>)._actionStatsLogged = true;\n const presentFields = ACTION_STATS_FIELDS.filter(f => normalized[f as keyof InsightRow] !== undefined);\n logger.debug(\"meta\", \"Normalize action stats fields present in row\", { presentFields });\n for (const field of presentFields) {\n const val = normalized[field as keyof InsightRow];\n logger.debug(\"meta\", \"Normalize action stats field sample\", {\n field,\n type: Array.isArray(val) ? `Array(${(val as unknown[]).length})` : typeof val,\n sample: Array.isArray(val) && (val as AdsActionStats[]).length > 0\n ? (val as AdsActionStats[]).slice(0, 3).map(a => `${a.action_type}: ${a.value}`)\n : val,\n });\n }\n }\n\n for (const field of ACTION_STATS_FIELDS) {\n const value = normalized[field as keyof InsightRow];\n if (Array.isArray(value)) {\n const numValue = extractValueFromActionStats(value as AdsActionStats[]);\n if (numValue !== undefined) {\n normalized[field] = numValue;\n }\n }\n }\n\n // Extract COUNT metrics from actions array (purchases, add_to_cart, etc.)\n if (normalized.actions && Array.isArray(normalized.actions)) {\n const actions = normalized.actions as AdsActionStats[];\n\n for (const [metricKey, actionType] of Object.entries(ACTIONS_COUNT_METRICS)) {\n if (normalized[metricKey] === undefined) {\n const value = getActionValue(actions, actionType);\n // Include zero values - they are valid data, not missing data\n if (value >= 0) {\n normalized[metricKey] = value;\n }\n }\n }\n\n // Debug: Log video_views_3s extraction specifically\n if (!(\"_video3sLogged\" in normalizeRowForDisplay)) {\n (normalizeRowForDisplay as unknown as Record<string, boolean>)._video3sLogged = true;\n const videoViewAction = actions.find(a => a.action_type === \"video_view\");\n logger.debug(\"meta\", \"Normalize video_views_3s extraction from actions\", {\n video_view_action_found: !!videoViewAction,\n video_view_raw_value: videoViewAction?.value,\n video_views_3s_stored: normalized.video_views_3s,\n actions_with_video: actions.filter(a => a.action_type?.includes(\"video\")).map(a => ({ action_type: a.action_type, value: a.value })),\n });\n }\n }\n\n // Extract MONETARY values from action_values array (purchase_value, etc.)\n if (normalized.action_values && Array.isArray(normalized.action_values)) {\n const actionValues = normalized.action_values as AdsActionStats[];\n\n // Debug: Log first row's action_values for purchase\n if (!(\"_actionValuesLogged\" in normalizeRowForDisplay)) {\n (normalizeRowForDisplay as unknown as Record<string, boolean>)._actionValuesLogged = true;\n const purchaseValue = actionValues.find(a =>\n a.action_type === \"offsite_conversion.fb_pixel_purchase\" || a.action_type === \"omni_purchase\"\n );\n logger.debug(\"meta\", \"Normalize action_values sample\", {\n totalActionValues: actionValues.length,\n purchaseValueEntry: purchaseValue || \"(not found)\",\n allActionTypes: actionValues.slice(0, 5).map(a => a.action_type),\n });\n }\n\n for (const [metricKey, actionType] of Object.entries(ACTION_VALUES_METRICS)) {\n if (normalized[metricKey] === undefined) {\n const value = getActionMonetaryValue(actionValues, actionType);\n // Include zero values - they are valid data, not missing data\n if (value >= 0) {\n normalized[metricKey] = value;\n }\n }\n }\n }\n\n // Extract COST PER ACTION metrics from cost_per_action_type array\n // Bug #9 Fix: Without this, all cost_per_* metrics would show the same value (value[0] of the array)\n if (normalized.cost_per_action_type && Array.isArray(normalized.cost_per_action_type)) {\n const costPerActions = normalized.cost_per_action_type as AdsActionStats[];\n\n // Debug: Log first row's cost_per_action_type for debugging\n if (!(\"_costPerActionLogged\" in normalizeRowForDisplay)) {\n (normalizeRowForDisplay as unknown as Record<string, boolean>)._costPerActionLogged = true;\n logger.debug(\"meta\", \"Normalize cost_per_action_type action types\", {\n totalEntries: costPerActions.length,\n allActionTypes: costPerActions.map(a => a.action_type),\n });\n }\n\n for (const [metricKey, actionTypes] of Object.entries(COST_PER_ACTION_METRICS)) {\n if (normalized[metricKey] === undefined) {\n // Try each action type in order until we find a match\n for (const actionType of actionTypes) {\n const action = costPerActions.find((a) => a.action_type === actionType);\n if (action) {\n const value = parseFloat(action.value);\n if (!isNaN(value) && value >= 0) {\n normalized[metricKey] = value;\n break; // Found a match, stop searching\n }\n }\n }\n }\n }\n }\n\n // Normalize VIDEO metrics (video_p25_watched_actions, video_p50_watched_actions, etc.)\n // Debug: log first row's video metrics processing\n if (!(\"_videoMetricsLogged\" in normalizeRowForDisplay)) {\n (normalizeRowForDisplay as unknown as Record<string, boolean>)._videoMetricsLogged = true;\n const videoFieldsPresent = VIDEO_METRICS_FIELDS.filter(f => normalized[f as keyof InsightRow] !== undefined);\n logger.debug(\"meta\", \"Normalize video metrics in row\", {\n fieldsPresent: videoFieldsPresent,\n video_time_watched_raw: normalized.video_time_watched_actions,\n video_30_sec_raw: normalized.video_30_sec_watched_actions,\n video_p25_sample: normalized.video_p25_watched_actions,\n });\n // Detailed debug for Total Watch Time\n if (normalized.video_time_watched_actions) {\n const vtw = normalized.video_time_watched_actions as AdsActionStats[] | number;\n if (Array.isArray(vtw)) {\n logger.debug(\"meta\", \"Normalize video_time_watched_actions array details\", {\n length: vtw.length,\n items: vtw.slice(0, 5).map(a => ({ action_type: a.action_type, value: a.value })),\n extracted: getVideoMetricValue(vtw),\n });\n } else {\n logger.debug(\"meta\", \"Normalize video_time_watched_actions is not array\", { type: typeof vtw, value: vtw });\n }\n } else {\n logger.debug(\"meta\", \"Normalize video_time_watched_actions is undefined in row\");\n }\n }\n\n for (const field of VIDEO_METRICS_FIELDS) {\n const value = normalized[field as keyof InsightRow];\n if (Array.isArray(value)) {\n const numValue = getVideoMetricValue(value as AdsActionStats[]);\n // Include zero values - they are valid data\n if (numValue >= 0) {\n normalized[field] = numValue;\n }\n\n // Debug: Log video_30_sec extraction specifically\n if (field === \"video_30_sec_watched_actions\" && !(\"_video30sLogged\" in normalizeRowForDisplay)) {\n (normalizeRowForDisplay as unknown as Record<string, boolean>)._video30sLogged = true;\n const actions30s = value as AdsActionStats[];\n logger.debug(\"meta\", \"Normalize video_30_sec_watched_actions extraction\", {\n array_length: actions30s.length,\n all_items: actions30s.map(a => ({ action_type: a.action_type, value: a.value })),\n extracted_value: numValue,\n stored_as: \"video_30_sec_watched_actions\",\n });\n }\n } else if (typeof value === \"number\" || typeof value === \"string\") {\n // Value is already a number or string - parse it\n const numValue = parseFloat(String(value));\n if (!isNaN(numValue)) {\n normalized[field] = numValue;\n }\n }\n }\n\n // FALLBACK: Calculate Total Watch Time if not returned by API\n // Meta API sometimes doesn't return video_time_watched_actions, but we can estimate it\n // from: Total Watch Time ≈ Average Watch Time × Video Views\n const currentTotalWatchTime = normalized.video_time_watched_actions;\n const totalWatchTimeIsMissing =\n currentTotalWatchTime === undefined ||\n (typeof currentTotalWatchTime === \"number\" && currentTotalWatchTime === 0) ||\n (Array.isArray(currentTotalWatchTime) && currentTotalWatchTime.length === 0);\n\n if (\n totalWatchTimeIsMissing &&\n typeof normalized.video_avg_time_watched_actions === \"number\" &&\n normalized.video_avg_time_watched_actions > 0 &&\n typeof normalized.video_views_3s === \"number\" &&\n normalized.video_views_3s > 0\n ) {\n const estimatedTotalWatchTime = normalized.video_avg_time_watched_actions * normalized.video_views_3s;\n (normalized as Record<string, unknown>).video_time_watched_actions = estimatedTotalWatchTime;\n\n // Log once for debugging\n if (!(\"_totalWatchTimeFallback\" in normalizeRowForDisplay)) {\n (normalizeRowForDisplay as unknown as Record<string, boolean>)._totalWatchTimeFallback = true;\n logger.debug(\"meta\", \"Normalize total watch time fallback calculated\", {\n video_avg_time_watched: normalized.video_avg_time_watched_actions,\n video_views_3s: normalized.video_views_3s,\n estimated_total_watch_time: estimatedTotalWatchTime,\n note: \"video_time_watched_actions not returned by API, using estimate\",\n });\n }\n }\n\n // Extract ENGAGEMENT metrics from actions array\n if (normalized.actions && Array.isArray(normalized.actions)) {\n const actions = normalized.actions as AdsActionStats[];\n\n for (const [metricKey, actionType] of Object.entries(ENGAGEMENT_ACTION_TYPES)) {\n if (normalized[metricKey] === undefined) {\n const value = getActionValue(actions, actionType);\n // Include zero values - they are valid data\n if (value >= 0) {\n normalized[metricKey] = value;\n }\n }\n }\n }\n\n // Debug: Final comparison of 3s vs 30s video views\n if (!(\"_videoComparisonLogged\" in normalizeRowForDisplay)) {\n (normalizeRowForDisplay as unknown as Record<string, boolean>)._videoComparisonLogged = true;\n const val3s = typeof normalized.video_views_3s === \"number\" ? normalized.video_views_3s : 0;\n const val30s = typeof normalized.video_30_sec_watched_actions === \"number\" ? normalized.video_30_sec_watched_actions : 0;\n logger.debug(\"meta\", \"Normalize video views comparison\", {\n \"3_second_views (video_views_3s)\": val3s,\n \"30_second_views (video_30_sec_watched_actions)\": val30s,\n dataKey_for_3s: \"video_views_3s\",\n dataKey_for_30s: \"video_30_sec_watched_actions\",\n logically_valid: val3s >= val30s,\n potential_bug: val30s > val3s ? \"30s > 3s - VALUES MAY BE SWAPPED!\" : \"OK\",\n });\n }\n\n return normalized;\n}\n\n/**\n * Normalize all rows in a data array for display\n * Extracts numeric values from AdsActionStats[] arrays\n * @param data - Array of InsightRow from API\n * @param attribution - Optional attribution settings (default: 7d click + 1d view)\n * @param displayMode - Optional display mode for attribution data\n */\nexport function normalizeDataForDisplay(\n data: InsightRow[],\n attribution?: AttributionSettings,\n displayMode?: AttributionDisplayMode\n): (InsightRow & Record<string, unknown>)[] {\n const settings = attribution || DEFAULT_ATTRIBUTION;\n const mode = displayMode || \"combined\";\n\n return data.map((row) => normalizeRowWithAttribution(row, settings, mode));\n}\n\n/**\n * Normalize a row with specific attribution settings\n */\nfunction normalizeRowWithAttribution(\n row: InsightRow,\n attribution: AttributionSettings,\n displayMode: AttributionDisplayMode\n): InsightRow & Record<string, unknown> {\n // First apply the basic normalization\n const normalized = normalizeRowForDisplay(row);\n\n // If using default combined mode, just use the default values\n if (displayMode === \"combined\") {\n // The API default value is already 7d_click + 1d_view, which matches our default\n // Only recalculate if using non-default attribution settings\n if (\n attribution.clickWindow !== \"7d_click\" ||\n attribution.viewWindow !== \"1d_view\" ||\n !attribution.includeViewThrough\n ) {\n // Re-extract conversion metrics with custom attribution\n applyCustomAttribution(normalized, attribution);\n }\n return normalized;\n }\n\n // Handle split view mode - add _click, _view, _total columns\n if (displayMode === \"split\") {\n applySplitAttribution(normalized, attribution);\n return normalized;\n }\n\n // Handle click_only or view_only mode\n if (displayMode === \"click_only\") {\n applyClickOnlyAttribution(normalized, attribution);\n return normalized;\n }\n\n if (displayMode === \"view_only\") {\n applyViewOnlyAttribution(normalized, attribution);\n return normalized;\n }\n\n return normalized;\n}\n\n/**\n * Apply custom attribution settings to conversion metrics\n */\nfunction applyCustomAttribution(\n row: InsightRow & Record<string, unknown>,\n attribution: AttributionSettings\n): void {\n const actions = row.actions as AdsActionStats[] | undefined;\n const actionValues = row.action_values as AdsActionStats[] | undefined;\n\n // Re-calculate count metrics with attribution\n // Skip non-conversion metrics (video views, engagement metrics) - they should always use total value\n for (const [metricKey, actionType] of Object.entries(ACTIONS_COUNT_METRICS)) {\n if (NON_ATTRIBUTION_METRICS.has(metricKey)) {\n // Skip - these metrics should not have attribution applied\n continue;\n }\n const value = getActionValueWithAttribution(actions, actionType, attribution);\n // Include zero values - they are valid data, not missing data\n if (value >= 0) {\n row[metricKey] = value;\n }\n }\n\n // Re-calculate monetary metrics with attribution\n for (const [metricKey, actionType] of Object.entries(ACTION_VALUES_METRICS)) {\n const value = getActionMonetaryValueWithAttribution(actionValues, actionType, attribution);\n // Include zero values - they are valid data, not missing data\n if (value >= 0) {\n row[metricKey] = value;\n }\n }\n\n // Re-calculate cost_per_action_type metrics with attribution\n const costPerActions = row.cost_per_action_type;\n if (costPerActions && Array.isArray(costPerActions)) {\n const costActions = costPerActions as AdsActionStats[];\n for (const [metricKey, actionTypes] of Object.entries(COST_PER_ACTION_METRICS)) {\n for (const actionType of actionTypes) {\n const action = costActions.find((a) => a.action_type === actionType);\n if (action) {\n const clickValue = parseFloat(action[attribution.clickWindow] || \"0\");\n const viewValue = attribution.includeViewThrough\n ? parseFloat(action[attribution.viewWindow] || \"0\")\n : 0;\n // For cost-per metrics, use the click window value (not sum of click+view)\n // CPA per window is already a ratio; combined CPA = spend / combined_conversions\n // Fallback: if per-window CPA isn't available, recalculate from base metrics\n if (clickValue > 0 || viewValue > 0) {\n // Recalculate from base: CPA = spend / conversions(combined_windows)\n const spend = parseFloat(String(row.spend ?? 0));\n const baseMetricKey = metricKey.replace(\"cost_per_\", \"\");\n const conversions = typeof row[baseMetricKey] === \"number\" ? (row[baseMetricKey] as number) : 0;\n if (spend > 0 && conversions > 0) {\n row[metricKey] = spend / conversions;\n }\n }\n break;\n }\n }\n }\n }\n\n // Re-calculate ROAS metrics from corrected base values\n const spend = parseFloat(String(row.spend ?? 0));\n if (spend > 0) {\n const purchaseValue = typeof row.purchase_value === \"number\" ? row.purchase_value : 0;\n if (purchaseValue > 0) {\n (row as Record<string, unknown>).purchase_roas = purchaseValue / spend;\n (row as Record<string, unknown>).website_purchase_roas = purchaseValue / spend;\n }\n }\n}\n\n/**\n * Apply split attribution - adds _click, _view, _total columns for each conversion metric\n */\nfunction applySplitAttribution(\n row: InsightRow & Record<string, unknown>,\n attribution: AttributionSettings\n): void {\n const actions = row.actions as AdsActionStats[] | undefined;\n const actionValues = row.action_values as AdsActionStats[] | undefined;\n\n // Split count metrics\n // Skip non-conversion metrics (video views, engagement metrics) - they should not be split by attribution\n for (const [metricKey, actionType] of Object.entries(ACTIONS_COUNT_METRICS)) {\n if (NON_ATTRIBUTION_METRICS.has(metricKey)) {\n // Skip - these metrics should not have attribution applied\n continue;\n }\n const split = getActionValuesSplit(actions, actionType, attribution);\n // Include zero values - they are valid data, not missing data\n if (split.total >= 0) {\n row[`${metricKey}_click`] = split.click;\n row[`${metricKey}_view`] = split.view;\n row[`${metricKey}_total`] = split.total;\n row[metricKey] = split.total; // Keep the main metric as total\n }\n }\n\n // Split monetary metrics\n for (const [metricKey, actionType] of Object.entries(ACTION_VALUES_METRICS)) {\n const split = getActionMonetaryValuesSplit(actionValues, actionType, attribution);\n // Include zero values - they are valid data, not missing data\n if (split.total >= 0) {\n row[`${metricKey}_click`] = split.click;\n row[`${metricKey}_view`] = split.view;\n row[`${metricKey}_total`] = split.total;\n row[metricKey] = split.total; // Keep the main metric as total\n }\n }\n}\n\n/**\n * Apply click-only attribution\n */\nfunction applyClickOnlyAttribution(\n row: InsightRow & Record<string, unknown>,\n attribution: AttributionSettings\n): void {\n const clickOnlyAttribution: AttributionSettings = {\n ...attribution,\n includeViewThrough: false,\n };\n applyCustomAttribution(row, clickOnlyAttribution);\n}\n\n/**\n * Apply view-only attribution\n */\nfunction applyViewOnlyAttribution(\n row: InsightRow & Record<string, unknown>,\n attribution: AttributionSettings\n): void {\n const actions = row.actions as AdsActionStats[] | undefined;\n const actionValues = row.action_values as AdsActionStats[] | undefined;\n\n // Extract only view-through values\n // Skip non-conversion metrics (video views, engagement metrics) - they should always use total value\n for (const [metricKey, actionTypes] of Object.entries(ACTIONS_COUNT_METRICS)) {\n if (NON_ATTRIBUTION_METRICS.has(metricKey)) {\n // Skip - these metrics should not have attribution applied\n continue;\n }\n if (actions) {\n // Try each action type in order\n for (const actionType of actionTypes) {\n const action = actions.find((a) => a.action_type === actionType);\n if (action) {\n const value = parseFloat(action[attribution.viewWindow] || \"0\");\n // Include zero values - they are valid data, not missing data\n if (value >= 0) {\n row[metricKey] = value;\n }\n break; // Found a match, stop searching\n }\n }\n }\n }\n\n for (const [metricKey, actionTypes] of Object.entries(ACTION_VALUES_METRICS)) {\n if (actionValues) {\n // Try each action type in order\n for (const actionType of actionTypes) {\n const action = actionValues.find((a) => a.action_type === actionType);\n if (action) {\n const value = parseFloat(action[attribution.viewWindow] || \"0\");\n // Include zero values - they are valid data, not missing data\n if (value >= 0) {\n row[metricKey] = value;\n }\n break; // Found a match, stop searching\n }\n }\n }\n }\n}\n\n// ============================================\n// DATA FORMATTING UTILITIES\n// ============================================\n\n/**\n * Format a number as currency\n */\nexport function formatCurrency(\n value: number,\n currency: string = \"EUR\",\n locale: string = \"fr-FR\"\n): string {\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(value);\n}\n\n/**\n * Format a number as percentage\n */\nexport function formatPercentage(\n value: number,\n decimals: number = 2\n): string {\n return `${value.toFixed(decimals)}%`;\n}\n\n/**\n * Format a large number with abbreviation\n */\nexport function formatNumber(value: number): string {\n if (value >= 1000000) {\n return `${(value / 1000000).toFixed(1)}M`;\n }\n if (value >= 1000) {\n return `${(value / 1000).toFixed(1)}K`;\n }\n return value.toLocaleString();\n}\n\n/**\n * Format duration in seconds to human readable\n */\nexport function formatDuration(seconds: number): string {\n if (seconds < 60) {\n return `${seconds.toFixed(1)}s`;\n }\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes}m ${remainingSeconds.toFixed(0)}s`;\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) 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 { MetaClient } from \"./client.js\";\n\ntype ToolResult = { content: Array<{ type: \"text\"; text: string }> };\ntype SuccessResponder = (data: unknown) => ToolResult;\n\nconst graphIdSchema = z.string().trim().min(1).max(200)\n .regex(/^[A-Za-z0-9_.:-]+$/, \"Use a Graph node ID without slashes or query parameters\");\nconst graphFieldSchema = z.string().trim().min(1).max(100)\n .regex(/^[A-Za-z][A-Za-z0-9_]*$/, \"Use a plain Graph field name\");\nconst graphDimensionSchema = z.string().trim().min(1).max(100)\n .regex(/^[A-Za-z][A-Za-z0-9_]*$/, \"Use a plain Insights field or breakdown name\");\nconst dateSchema = z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"Expected YYYY-MM-DD\");\n\nexport const META_BROAD_READ_EDGES = [\n \"accounts\",\n \"activities\",\n \"ad_studies\",\n \"adcreatives\",\n \"adimages\",\n \"adspixels\",\n \"advideos\",\n \"ads\",\n \"adsets\",\n \"assigned_pages\",\n \"assigned_ad_accounts\",\n \"campaigns\",\n \"client_ad_accounts\",\n \"client_data_sets\",\n \"client_instagram_accounts\",\n \"client_pages\",\n \"client_pixels\",\n \"client_product_catalogs\",\n \"client_publisher_block_lists\",\n \"customaudiences\",\n \"customconversions\",\n \"delivery_estimate\",\n \"feed\",\n \"instagram_accounts\",\n \"media\",\n \"leadgen_forms\",\n \"offline_conversion_data_sets\",\n \"owned_ad_accounts\",\n \"owned_data_sets\",\n \"owned_instagram_accounts\",\n \"owned_pages\",\n \"owned_pixels\",\n \"owned_product_catalogs\",\n \"owned_publisher_block_lists\",\n \"pages\",\n \"pixels\",\n \"posts\",\n \"product_catalogs\",\n \"product_sets\",\n \"products\",\n \"publisher_block_lists\",\n \"reachestimate\",\n \"reachfrequencypredictions\",\n \"saved_audiences\",\n \"videos\",\n \"cells\",\n \"objectives\",\n] as const;\n\nconst readEdgeSchema = z.enum(META_BROAD_READ_EDGES);\n\nconst filterValueSchema = z.union([\n z.string().max(2_000),\n z.number().finite(),\n z.boolean(),\n z.array(z.union([z.string().max(500), z.number().finite(), z.boolean()])).max(500),\n]);\n\nconst graphFilterSchema = z.object({\n field: z.string().trim().min(1).max(150)\n .regex(/^[A-Za-z][A-Za-z0-9_.]*$/, \"Invalid filtering field\"),\n operator: z.enum([\n \"EQUAL\", \"NOT_EQUAL\", \"GREATER_THAN\", \"GREATER_THAN_OR_EQUAL\",\n \"LESS_THAN\", \"LESS_THAN_OR_EQUAL\", \"IN_RANGE\", \"NOT_IN_RANGE\",\n \"CONTAIN\", \"NOT_CONTAIN\", \"IN\", \"NOT_IN\", \"STARTS_WITH\", \"ENDS_WITH\",\n \"ANY\", \"ALL\", \"NONE\",\n ]),\n value: filterValueSchema,\n});\n\nconst datePresetSchema = z.enum([\n \"today\", \"yesterday\", \"this_month\", \"last_month\", \"this_quarter\",\n \"maximum\", \"data_maximum\", \"last_3d\", \"last_7d\", \"last_14d\",\n \"last_28d\", \"last_30d\", \"last_90d\", \"last_week_mon_sun\",\n \"last_week_sun_sat\", \"last_quarter\", \"last_year\",\n \"this_week_mon_today\", \"this_week_sun_today\", \"this_year\",\n]);\n\nconst targetingSearchTypeSchema = z.enum([\n \"adinterest\",\n \"adbehaviors\",\n \"adinterestsuggestion\",\n \"adinterestvalid\",\n \"adgeolocation\",\n \"adgeolocationmeta\",\n \"adlocale\",\n \"adcountry\",\n \"adcity\",\n \"adregion\",\n \"admarket\",\n \"adzipcode\",\n \"adTargetingCategory\",\n \"adworkemployer\",\n \"adworkposition\",\n \"adeducationschool\",\n \"adeducationmajor\",\n]);\n\nexport function buildMetaGraphReadUrl(\n apiVersion: string,\n path: string,\n params: Record<string, string | number | boolean | undefined>,\n): string {\n const normalizedPath = path.replace(/^\\/+/, \"\");\n if (!normalizedPath || normalizedPath.includes(\"..\") || /[?#]/.test(normalizedPath)) {\n throw new Error(\"Invalid Meta Graph read path.\");\n }\n const url = new URL(`https://graph.facebook.com/${apiVersion}/${normalizedPath}`);\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== \"\") url.searchParams.set(key, String(value));\n }\n return url.toString();\n}\n\nexport function encodeExtraGraphParams(rawParams: Record<string, unknown> | undefined): Record<string, string> {\n if (!rawParams) return {};\n const entries = Object.entries(rawParams);\n if (entries.length > 50) throw new Error(\"At most 50 extra Graph parameters are allowed.\");\n const blocked = new Set([\n \"access_token\", \"appsecret_proof\", \"signed_request\", \"method\", \"http_method\", \"batch\",\n \"fields\", \"filtering\", \"since\", \"until\", \"limit\", \"after\", \"before\", \"summary\",\n ]);\n const encoded: Record<string, string> = {};\n for (const [key, value] of entries) {\n if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(key)) throw new Error(`Invalid Graph parameter key: ${key}`);\n if (blocked.has(key.toLowerCase())) throw new Error(`Graph parameter ${key} must use the dedicated input or is blocked for safety.`);\n if (value === undefined || value === null || typeof value === \"function\" || typeof value === \"symbol\" || typeof value === \"bigint\") {\n throw new Error(`Graph parameter ${key} must be a JSON-compatible scalar, array, or object.`);\n }\n const rendered = typeof value === \"object\" ? JSON.stringify(value) : String(value);\n if (rendered.length > 10_000) throw new Error(`Graph parameter ${key} exceeds 10,000 characters.`);\n encoded[key] = rendered;\n }\n if (JSON.stringify(encoded).length > 25_000) throw new Error(\"Encoded extra Graph parameters exceed 25,000 characters.\");\n return encoded;\n}\n\nfunction requireOneTimeSelector(datePreset: string | undefined, timeRange: { since: string; until: string } | undefined): void {\n if (datePreset && timeRange) {\n throw new Error(\"Pass either datePreset or timeRange, not both.\");\n }\n if (timeRange && timeRange.since > timeRange.until) {\n throw new Error(\"timeRange.since must be on or before timeRange.until.\");\n }\n}\n\nexport function registerMetaBroadReadTools(\n server: McpServer,\n client: MetaClient,\n ok: SuccessResponder,\n): void {\n server.tool(\n \"meta_get_node_fields\",\n \"Read arbitrary flat fields from one Meta Graph node. This GET-only escape hatch covers newly released campaign, ad set, ad, creative, audience, catalog, Page, Instagram, pixel, and other accessible node fields without waiting for a fixed MCP schema update.\",\n {\n nodeId: graphIdSchema.describe(\"Graph node ID, for example a campaign, ad set, ad, creative, Page, IG account, pixel, audience, or catalog ID\"),\n fields: z.array(graphFieldSchema).min(1).max(100).describe(\"Flat Graph field names; nested field expansion is intentionally disabled\"),\n includeMetadata: z.boolean().optional().default(false).describe(\"Ask Graph for field metadata when supported\"),\n },\n async ({ nodeId, fields, includeMetadata }) => {\n try {\n const url = buildMetaGraphReadUrl(client.apiVersion, nodeId, {\n fields: [...new Set(fields)].join(\",\"),\n metadata: includeMetadata ? 1 : undefined,\n });\n const data = await client.fetchUrl(url);\n return ok({ data, nodeId, requestedFields: [...new Set(fields)] });\n } catch (error) {\n return formatMcpToolError(error);\n }\n },\n );\n\n server.tool(\n \"meta_list_edge_raw\",\n \"List an allowlisted read-only Meta Graph edge with caller-selected flat fields, filters, and cursor pagination. Use this for broad metadata/entity discovery when a specialized tool does not expose a newly added field.\",\n {\n parentId: graphIdSchema.describe(\"Parent Graph node ID, such as act_123, a Business, Page, catalog, campaign, or ad set ID\"),\n edge: readEdgeSchema,\n fields: z.array(graphFieldSchema).min(1).max(100).optional(),\n filtering: z.array(graphFilterSchema).max(20).optional(),\n parameters: z.record(z.unknown()).optional().describe(\"Additional documented GET parameters, for example targeting_spec or optimization_goal; auth and HTTP method override parameters are blocked\"),\n since: dateSchema.optional(),\n until: dateSchema.optional(),\n limit: z.number().int().min(1).max(500).optional().default(100),\n after: z.string().max(2_000).optional(),\n before: z.string().max(2_000).optional(),\n includeSummary: z.boolean().optional().default(false),\n },\n async ({ parentId, edge, fields, filtering, parameters, since, until, limit, after, before, includeSummary }) => {\n try {\n if (since && until && since > until) throw new Error(\"since must be on or before until.\");\n if (after && before) throw new Error(\"Pass either after or before, not both.\");\n const url = buildMetaGraphReadUrl(client.apiVersion, `${parentId}/${edge}`, {\n ...encodeExtraGraphParams(parameters),\n fields: fields ? [...new Set(fields)].join(\",\") : undefined,\n filtering: filtering?.length ? JSON.stringify(filtering) : undefined,\n since,\n until,\n limit,\n after,\n before,\n summary: includeSummary ? \"true\" : undefined,\n });\n const data = await client.fetchUrl(url);\n return ok({ data, parentId, edge, requestedFields: fields ?? [], paging: (data as Record<string, unknown>).paging });\n } catch (error) {\n return formatMcpToolError(error);\n }\n },\n );\n\n server.tool(\n \"meta_get_insights_raw\",\n \"Query the Meta Insights edge with validated native field names, breakdowns, action breakdowns, attribution windows, filters, sort, summary, and pagination. This complements meta_get_insights when Meta adds fields before the curated metric catalog is updated.\",\n {\n objectId: graphIdSchema.describe(\"Ad account (act_...), campaign, ad set, or ad ID whose /insights edge should be queried\"),\n fields: z.array(graphDimensionSchema).min(1).max(100).describe(\"Native Meta Insights API fields; calculated aliases are not accepted here\"),\n level: z.enum([\"account\", \"campaign\", \"adset\", \"ad\"]).optional(),\n breakdowns: z.array(graphDimensionSchema).max(20).optional(),\n actionBreakdowns: z.array(graphDimensionSchema).max(20).optional(),\n actionAttributionWindows: z.array(z.enum([\"1d_click\", \"7d_click\", \"28d_click\", \"1d_view\", \"1d_ev\", \"dda\", \"skan_click\", \"skan_view\"])).max(8).optional(),\n datePreset: datePresetSchema.optional(),\n timeRange: z.object({ since: dateSchema, until: dateSchema }).optional(),\n timeIncrement: z.union([z.number().int().min(1).max(90), z.enum([\"monthly\", \"all_days\"]) ]).optional(),\n filtering: z.array(graphFilterSchema).max(20).optional(),\n sort: z.string().trim().max(150).regex(/^[A-Za-z][A-Za-z0-9_.]*_(ascending|descending)$/).optional(),\n limit: z.number().int().min(1).max(500).optional().default(100),\n after: z.string().max(2_000).optional(),\n includeSummary: z.boolean().optional().default(false),\n },\n async ({ objectId, fields, level, breakdowns, actionBreakdowns, actionAttributionWindows, datePreset, timeRange, timeIncrement, filtering, sort, limit, after, includeSummary }) => {\n try {\n requireOneTimeSelector(datePreset, timeRange);\n const url = buildMetaGraphReadUrl(client.apiVersion, `${objectId}/insights`, {\n fields: [...new Set(fields)].join(\",\"),\n level,\n breakdowns: breakdowns?.length ? [...new Set(breakdowns)].join(\",\") : undefined,\n action_breakdowns: actionBreakdowns?.length ? [...new Set(actionBreakdowns)].join(\",\") : undefined,\n action_attribution_windows: actionAttributionWindows?.length ? [...new Set(actionAttributionWindows)].join(\",\") : undefined,\n date_preset: datePreset,\n time_range: timeRange ? JSON.stringify(timeRange) : undefined,\n time_increment: timeIncrement,\n filtering: filtering?.length ? JSON.stringify(filtering) : undefined,\n sort,\n limit,\n after,\n summary: includeSummary ? \"true\" : undefined,\n });\n const data = await client.fetchUrl(url);\n return ok({\n data: (data as Record<string, unknown>).data ?? data,\n paging: (data as Record<string, unknown>).paging,\n summary: (data as Record<string, unknown>).summary,\n nativeFields: true,\n });\n } catch (error) {\n return formatMcpToolError(error);\n }\n },\n );\n\n server.tool(\n \"meta_search_targeting_options\",\n \"Search Meta's read-only targeting metadata for interests, validated interests, geographies, locales, countries, cities, regions, markets, or postal codes.\",\n {\n type: targetingSearchTypeSchema,\n query: z.string().trim().min(1).max(200).optional().describe(\"Search text; required by most interest and geography searches\"),\n countryCode: z.string().trim().length(2).transform((value) => value.toUpperCase()).optional(),\n locationTypes: z.array(z.enum([\"country\", \"country_group\", \"region\", \"city\", \"zip\", \"geo_market\", \"electoral_district\"])).max(7).optional(),\n limit: z.number().int().min(1).max(1000).optional().default(100),\n },\n async ({ type, query, countryCode, locationTypes, limit }) => {\n try {\n if ((type === \"adinterest\" || type === \"adinterestvalid\") && !query) {\n throw new Error(`${type} requires query.`);\n }\n const url = buildMetaGraphReadUrl(client.apiVersion, \"search\", {\n type,\n q: query,\n country_code: countryCode,\n location_types: locationTypes?.length ? JSON.stringify(locationTypes) : undefined,\n limit,\n });\n const data = await client.fetchUrl(url);\n return ok({ data: (data as Record<string, unknown>).data ?? data, paging: (data as Record<string, unknown>).paging, search: { type, query, countryCode, locationTypes } });\n } catch (error) {\n return formatMcpToolError(error);\n }\n },\n );\n\n server.tool(\n \"meta_get_ad_preview\",\n \"Get the read-only preview markup for an existing Meta ad in a requested placement format. The response may contain an iframe body but never creates or edits a creative.\",\n {\n adId: graphIdSchema,\n adFormat: z.string().trim().min(1).max(100).regex(/^[A-Z][A-Z0-9_]*$/).optional().default(\"DESKTOP_FEED_STANDARD\"),\n },\n async ({ adId, adFormat }) => {\n try {\n const preview = await client.getAdPreview(adId, adFormat);\n return ok({ adId, adFormat, preview, readOnly: true });\n } catch (error) {\n return formatMcpToolError(error);\n }\n },\n );\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) 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 { METRIC_CATALOG } from \"./metric-catalog.js\";\nimport { BREAKDOWN_CATALOG } from \"./breakdown-catalog.js\";\nimport { DIMENSION_CATALOG } from \"./dimension-catalog.js\";\n\nconst READ_ONLY_TOOLS = [\n \"meta_health_check\",\n \"meta_list_ad_accounts\",\n \"meta_get_account_details\",\n \"meta_get_business_assets\",\n \"meta_get_pages\",\n \"meta_get_instagram_accounts\",\n \"meta_get_pixels\",\n \"meta_get_campaigns\",\n \"meta_get_adsets\",\n \"meta_get_ads\",\n \"meta_get_ad_activity\",\n \"meta_get_delivery_diagnostics\",\n \"meta_get_creatives\",\n \"meta_get_creative_assets\",\n \"meta_get_insights\",\n \"meta_get_campaign_structure\",\n \"meta_get_audiences\",\n \"meta_get_audience_details\",\n \"meta_get_catalog_products\",\n \"meta_join_product_insights\",\n \"meta_get_brand_safety_controls\",\n \"meta_get_ad_studies\",\n \"meta_get_study_results\",\n \"meta_interpret_experiment_results\",\n \"meta_validate_query\",\n \"meta_get_page_posts\",\n \"meta_get_organic_content_enrichment\",\n \"meta_debug_token\",\n \"meta_search_entities\",\n \"meta_get_node_fields\",\n \"meta_list_edge_raw\",\n \"meta_get_insights_raw\",\n \"meta_search_targeting_options\",\n \"meta_get_ad_preview\",\n] as const;\n\nfunction jsonResource(uri: string, data: unknown) {\n return {\n contents: [\n {\n uri,\n mimeType: \"application/json\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n}\n\nexport function registerMetaResources(server: McpServer, enableWrites = false): void {\n server.resource(\"meta-metrics\", \"meta://metrics\", async () => ({\n contents: [\n {\n uri: \"meta://metrics\",\n mimeType: \"application/json\",\n text: JSON.stringify(\n METRIC_CATALOG.map((m) => ({\n key: m.key,\n name: m.name,\n description: m.description,\n category: m.category,\n type: m.type,\n format: m.format,\n })),\n null,\n 2,\n ),\n },\n ],\n }));\n\n server.resource(\"meta-breakdowns\", \"meta://breakdowns\", async () => ({\n contents: [\n {\n uri: \"meta://breakdowns\",\n mimeType: \"application/json\",\n text: JSON.stringify(\n BREAKDOWN_CATALOG.map((b) => ({\n key: b.key,\n name: b.name,\n description: b.description,\n category: b.category,\n })),\n null,\n 2,\n ),\n },\n ],\n }));\n\n server.resource(\"meta-compatibility\", \"meta://compatibility\", async () => {\n const compatMap: Record<string, string[]> = {};\n for (const m of METRIC_CATALOG) {\n compatMap[m.key] = m.compatibleBreakdowns;\n }\n return {\n contents: [\n {\n uri: \"meta://compatibility\",\n mimeType: \"application/json\",\n text: JSON.stringify(\n {\n description:\n \"Metric-to-breakdown compatibility. Each metric key maps to an array of compatible breakdown keys.\",\n metrics: compatMap,\n dimensions: typeof DIMENSION_CATALOG !== \"undefined\"\n ? DIMENSION_CATALOG.map((d: { key: string; name: string }) => ({\n key: d.key,\n name: d.name,\n }))\n : [],\n },\n null,\n 2,\n ),\n },\n ],\n };\n });\n\n server.resource(\"meta-manifest\", \"meta://manifest\", async () => jsonResource(\n \"meta://manifest\",\n {\n name: \"Meta Ads MCP\",\n platform: \"meta\",\n mode: \"read-only\",\n guarantees: [\n \"Tools perform Graph API GET requests only.\",\n \"Access tokens are never returned in tool responses.\",\n \"Permission-sensitive edges return actionable warnings when possible.\",\n ],\n required_scopes: [\"ads_read\"],\n recommended_scopes: [\n \"business_management\",\n \"pages_show_list\",\n \"pages_read_engagement\",\n \"instagram_basic\",\n ],\n optional_workflow_scopes: [\n {\n scope: \"catalog_management\",\n used_for: \"GET-only Product Catalog/Product Item reads when Meta requires catalog-level permission.\",\n },\n ],\n write_scopes_not_required: [\"ads_management\"],\n tools: READ_ONLY_TOOLS,\n resources: [\n \"meta://metrics\",\n \"meta://breakdowns\",\n \"meta://compatibility\",\n \"meta://manifest\",\n \"meta://recipes\",\n \"meta://p2-readonly\",\n \"meta://app-review\",\n ],\n first_tool_to_run: \"meta_health_check\",\n },\n ));\n\n server.resource(\"meta-recipes\", \"meta://recipes\", async () => jsonResource(\n \"meta://recipes\",\n {\n recipes: [\n {\n name: \"Health and permission triage\",\n goal: \"Confirm the token works and identify missing read-only scopes.\",\n steps: [\n \"Run meta_health_check.\",\n \"If ads_read is missing, refresh the token with ads_read.\",\n \"If business/page/IG discovery is incomplete, add only the recommended read-only scopes needed for that workflow.\",\n ],\n },\n {\n name: \"Business asset map\",\n goal: \"Build an inventory of Business Manager assets without write scopes.\",\n steps: [\n \"Run meta_get_business_assets with businessId when known.\",\n \"Fallback to adAccountId to discover account pixels and related business metadata.\",\n \"Use warnings to request exact missing asset permissions from the Business Manager admin.\",\n ],\n },\n {\n name: \"Delivery diagnostics\",\n goal: \"Find paused, rejected, or issue-bearing campaigns/ad sets/ads.\",\n steps: [\n \"Run meta_get_delivery_diagnostics with level=all and a recent date preset.\",\n \"Inspect effective_status, issues_info when present, and simple_insights together.\",\n \"Use level-specific pagination when the account is large.\",\n ],\n },\n {\n name: \"Creative asset audit\",\n goal: \"Normalize creative URLs, CTA, page, and Instagram references for analysis.\",\n steps: [\n \"Run meta_get_creative_assets with adAccountId for broad inventory.\",\n \"Pass adIds or creativeIds for targeted enrichment.\",\n \"Use thumbnail_url, image_url, video_id, link_url, call_to_action, object_story_spec, and asset_feed_spec.\",\n ],\n },\n {\n name: \"Audience inventory\",\n goal: \"Read custom, saved, and lookalike audiences with pagination.\",\n steps: [\n \"Run meta_get_audience_details with type=all for a first pass.\",\n \"Use type=custom, type=saved, or type=lookalike with returned cursors for precise pagination.\",\n \"Treat privacy-limited fields as expected Meta behavior and follow warnings.\",\n ],\n },\n {\n name: \"Catalog product enrichment\",\n goal: \"Join product-breakdown insights to catalog metadata when catalog access is available.\",\n steps: [\n \"Run meta_get_catalog_products with businessId to discover catalogId, or pass catalogId directly.\",\n \"Run meta_join_product_insights with productBreakdown=product_id and the selected catalogId.\",\n \"Use catalog_join.matched and warnings to separate missing catalog access from true unmatched product IDs.\",\n ],\n },\n {\n name: \"Brand safety/context controls read\",\n goal: \"Inspect placement and context-control settings without modifying block lists or ad sets.\",\n steps: [\n \"Run meta_get_brand_safety_controls with adAccountId.\",\n \"Review adset_context_controls for publisher platforms, positions, excluded publisher categories, and brand safety filter levels.\",\n \"Treat block-list warnings as expected when the Graph edge is not exposed for the account or rollout.\",\n ],\n },\n {\n name: \"Experiment/lift interpretation\",\n goal: \"Read studies and interpret confidence without overclaiming results.\",\n steps: [\n \"Run meta_interpret_experiment_results with studyId for a targeted read or adAccountId to discover studies.\",\n \"Use interpretation.confidence_read and guardrail fields before declaring a winner.\",\n \"Keep results below 90% confidence as directional or inconclusive.\",\n ],\n },\n {\n name: \"Organic Page/IG enrichment\",\n goal: \"Collect existing Page posts and Instagram media context for creative analysis.\",\n steps: [\n \"Run meta_get_organic_content_enrichment with pageId and includeInsights=false for the broadest compatibility.\",\n \"Set includeInsights=true only when Page/IG insight permissions are expected.\",\n \"Use warnings to distinguish missing permissions from unavailable media-type metrics.\",\n ],\n },\n {\n name: \"Change activity review\",\n goal: \"Review account activity history for debugging performance changes.\",\n steps: [\n \"Run meta_get_ad_activity with adAccountId, since, until, and limit.\",\n \"Correlate event_time and actor with performance changes from meta_get_insights.\",\n \"Increase date range only when needed to avoid noisy activity logs.\",\n ],\n },\n {\n name: \"Forward-compatible Graph exploration\",\n goal: \"Read new native fields and allowlisted edges before the curated MCP catalogs catch up.\",\n steps: [\n \"Use meta_get_node_fields for flat fields on a known Graph node.\",\n \"Use meta_list_edge_raw for allowlisted entity/asset edges with cursor pagination.\",\n \"Use meta_get_insights_raw only for native Insights fields not yet present in meta://metrics.\",\n \"Use meta_search_targeting_options for interest, locale, and geography dictionaries.\",\n ],\n },\n ],\n },\n ));\n\n server.resource(\"meta-p2-readonly\", \"meta://p2-readonly\", async () => jsonResource(\n \"meta://p2-readonly\",\n {\n positioning: \"P2 tools extend read-only analysis coverage for catalog joins, brand safety/context controls, experiments/lift interpretation, and organic Page/IG enrichment.\",\n guarantees: [\n \"Every P2 tool performs Graph API GET requests only.\",\n \"P2 tools return warnings instead of failing hard when optional Graph edges are unavailable.\",\n \"P2 tools preserve access token redaction through the shared ok() response path.\",\n ],\n tools: [\n {\n name: \"meta_get_catalog_products\",\n purpose: \"Read product catalogs, product items, and optional product sets for join-ready metadata.\",\n common_inputs: [\"businessId\", \"catalogId\", \"productIds\"],\n common_warning: \"Catalog edges can require catalog asset access or catalog_management even for GET-only reads.\",\n },\n {\n name: \"meta_join_product_insights\",\n purpose: \"Query product-breakdown insights and enrich rows with catalog product groups.\",\n common_inputs: [\"adAccountId\", \"catalogId\", \"productBreakdown\", \"datePreset\"],\n common_warning: \"Product breakdown availability depends on objective, catalog setup, and metric compatibility.\",\n },\n {\n name: \"meta_get_brand_safety_controls\",\n purpose: \"Read ad set targeting context controls and optional publisher block-list metadata.\",\n common_inputs: [\"adAccountId\", \"businessId\", \"adsetIds\"],\n common_warning: \"Publisher block-list edges are rollout/version dependent and may only be visible in Business Manager UI.\",\n },\n {\n name: \"meta_interpret_experiment_results\",\n purpose: \"Read Ad Study cells/objectives and classify confidence with conservative lift-study guardrails.\",\n common_inputs: [\"studyId\", \"adAccountId\", \"includeCellEntities\"],\n common_warning: \"Full results payloads can be permission or whitelist dependent; basic objectives may still be available.\",\n },\n {\n name: \"meta_get_organic_content_enrichment\",\n purpose: \"Read Page posts and Instagram media with media URLs, permalinks, counts, attachments, and optional insights.\",\n common_inputs: [\"pageId\", \"instagramAccountId\", \"postIds\", \"mediaIds\"],\n common_warning: \"Organic insight metrics vary by Page/IG permission, media type, and Graph API version.\",\n },\n ],\n },\n ));\n\n server.resource(\"meta-app-review\", \"meta://app-review\", async () => jsonResource(\n \"meta://app-review\",\n {\n app_review_positioning: enableWrites\n ? \"Write tools are enabled on this instance. It reads Meta Ads entities and can also update campaign, ad set and ad status, budgets, schedules and names, and create paused campaigns. Every write returns a preview and applies only when the caller repeats the call with confirm: true.\"\n : \"This instance is read-only and does not create, update, pause, delete, or mutate Meta Ads entities. Write tools exist but are disabled unless META_ENABLE_WRITES is set.\",\n writes_enabled: enableWrites,\n minimum_scope_for_ads_reporting: \"ads_read\",\n ...(enableWrites\n ? { scope_required_for_writes: \"ads_management (required in addition to ads_read when writes are enabled)\" }\n : {}),\n optional_read_only_scopes: [\n {\n scope: \"business_management\",\n reason: \"Discover Business Manager assets such as businesses, pages, pixels, datasets, and partner/client assets when allowed.\",\n },\n {\n scope: \"pages_show_list\",\n reason: \"List Pages the token user can access for page and Instagram account discovery.\",\n },\n {\n scope: \"pages_read_engagement\",\n reason: \"Read Page metadata and posts for context; no publishing is performed.\",\n },\n {\n scope: \"instagram_basic\",\n reason: \"Read linked Instagram account profile metadata from Page or Business edges.\",\n },\n {\n scope: \"catalog_management\",\n reason: \"Optional for GET-only Product Catalog/Product Item reads when Meta requires catalog-level access. This server does not create, update, or delete catalog assets.\",\n },\n ],\n write_scopes_to_avoid_for_this_server: [\n \"ads_management\",\n \"pages_manage_posts\",\n \"pages_manage_metadata\",\n \"instagram_content_publish\",\n ],\n reviewer_test_flow: [\n \"Configure META_ACCESS_TOKEN with ads_read.\",\n \"Run meta_health_check and verify the response contains token metadata, scopes, and ad account count without the access token.\",\n \"Run meta_list_ad_accounts and meta_get_insights for read-only reporting.\",\n \"Optionally grant business/page/IG read scopes and run meta_get_business_assets, meta_get_pages, meta_get_instagram_accounts, and meta_get_pixels.\",\n \"Optionally grant catalog access and run meta_get_catalog_products or meta_join_product_insights; verify only GET reads occur.\",\n \"Optionally run meta_get_brand_safety_controls, meta_interpret_experiment_results, and meta_get_organic_content_enrichment to verify P2 read-only workflows.\",\n ],\n data_handling_notes: [\n \"Tool responses redact access_token fields and access_token query parameters.\",\n \"Permission failures are returned as warnings for discovery tools when a partial response is still useful.\",\n \"No write or mutation endpoints are registered by this server.\",\n ],\n },\n ));\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n/**\n * Write tools for the Meta Marketing API.\n *\n * These are registered only when `META_ENABLE_WRITES` is set. Reading needs the\n * `ads_read` permission; everything in this file needs `ads_management`.\n *\n * Part of meta-ads-mcp-server: https://github.com/getmcpads-com/meta-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 { MetaConfig } from \"../../config/types.js\";\nimport { DEFAULT_API_VERSION } 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 account, 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\n/**\n * Meta expects budgets in minor units, and the factor depends on the currency.\n * The yen and the won have no subunit, so sending 1250 for 12.50 there would\n * multiply the spend by a hundred.\n */\nconst ZERO_DECIMAL = new Set([\n \"JPY\", \"KRW\", \"CLP\", \"ISK\", \"VND\", \"UGX\", \"PYG\", \"RWF\", \"XOF\",\n \"XAF\", \"XPF\", \"BIF\", \"DJF\", \"GNF\", \"KMF\", \"MGA\", \"VUV\",\n]);\n\nexport function toMinorUnits(amount: number, currency: string): number {\n const code = currency.trim().toUpperCase();\n if (!/^[A-Z]{3}$/.test(code)) {\n throw new Error(`Expected a three letter currency code, received \"${currency}\".`);\n }\n if (!Number.isFinite(amount) || amount <= 0) {\n throw new Error(`Expected a positive amount, received \"${amount}\".`);\n }\n return Math.round(amount * (ZERO_DECIMAL.has(code) ? 1 : 100));\n}\n\nasync function request(url: string, init: RequestInit, context: string): Promise<unknown> {\n const response = await fetch(url, init);\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 Error(`${context}: ${detail.slice(0, 300)}`);\n }\n return parsed;\n}\n\nexport function registerMetaWrites(server: McpServer, config: MetaConfig): void {\n const version = config.apiVersion || DEFAULT_API_VERSION;\n const graph = (path: string) => `https://graph.facebook.com/${version}/${path}`;\n const form = (fields: Record<string, string>) => ({\n method: \"POST\" as const,\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ ...fields, access_token: config.accessToken }),\n });\n\n // ── Status: pause / reactivate ────────────────────────────────────\n const statusTool = (name: string, target: \"campaign\" | \"ad set\" | \"ad\", param: string) =>\n server.tool(\n name,\n `Pause or reactivate a Meta ${target}. Previews by default: without confirm: true, ` +\n `the tool describes the change without applying it.`,\n {\n [param]: z.string().describe(`${target} ID.`),\n adAccountId: z.string().describe(\"Owning ad account (act_… or bare ID).\"),\n status: z.enum([\"ACTIVE\", \"PAUSED\"]).describe(\"ACTIVE reactivates, PAUSED stops delivery.\"),\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const id = String(a[param]);\n const { status, confirm } = a as Record<string, string | boolean>;\n if (!confirm) return preview(name, { target: id, newStatus: status });\n const result = await request(graph(id), form({ status: String(status) }), `Meta ${target} status`);\n return ok({ applied: true, action: name, result });\n },\n );\n\n statusTool(\"meta_update_campaign_status\", \"campaign\", \"campaignId\");\n statusTool(\"meta_update_adset_status\", \"ad set\", \"adSetId\");\n statusTool(\"meta_update_ad_status\", \"ad\", \"adId\");\n\n // ── Rename ────────────────────────────────────────────────────────\n const renameTool = (name: string, target: \"campaign\" | \"ad set\" | \"ad\", param: string) =>\n server.tool(\n name,\n `Rename a Meta ${target}. The name is the only thing that changes. Previews by default.`,\n {\n [param]: z.string().describe(`${target} ID.`),\n adAccountId: z.string().describe(\"Owning ad account (act_… or bare ID).\"),\n name: z.string().min(1).max(400).describe(\"New name. This is the only thing the call changes.\"),\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const id = String(a[param]);\n const { name: newName, confirm } = a as Record<string, string | boolean>;\n if (!confirm) return preview(name, { target: id, newName });\n const result = await request(graph(id), form({ name: String(newName) }), `Meta ${target} rename`);\n return ok({ applied: true, action: name, result });\n },\n );\n\n renameTool(\"meta_rename_campaign\", \"campaign\", \"campaignId\");\n renameTool(\"meta_rename_adset\", \"ad set\", \"adSetId\");\n renameTool(\"meta_rename_ad\", \"ad\", \"adId\");\n\n // ── Create campaign (always paused) ───────────────────────────────\n server.tool(\n \"meta_create_campaign\",\n \"Create a Meta campaign. It is always created PAUSED and there is no option to create \" +\n \"it active. Previews by default.\",\n {\n adAccountId: z.string().describe(\"Ad account, act_… or bare ID.\"),\n name: z.string().min(1).max(400).describe(\"Campaign name.\"),\n objective: z\n .enum([\n \"OUTCOME_TRAFFIC\",\n \"OUTCOME_SALES\",\n \"OUTCOME_LEADS\",\n \"OUTCOME_AWARENESS\",\n \"OUTCOME_ENGAGEMENT\",\n \"OUTCOME_APP_PROMOTION\",\n ])\n .describe(\"Campaign objective.\"),\n specialAdCategories: z\n .array(z.enum([\"NONE\", \"HOUSING\", \"EMPLOYMENT\", \"CREDIT\", \"ISSUES_ELECTIONS_POLITICS\"]))\n .optional()\n .describe(\"Required by Meta. Defaults to none.\"),\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const { adAccountId, name, objective, specialAdCategories, confirm } = a;\n const act = String(adAccountId).startsWith(\"act_\") ? String(adAccountId) : `act_${adAccountId}`;\n const categories = Array.isArray(specialAdCategories) ? specialAdCategories : [];\n if (!confirm) {\n return preview(\"meta_create_campaign\", {\n adAccount: act, name, objective, specialAdCategories: categories, status: \"PAUSED\",\n });\n }\n const result = await request(\n graph(`${act}/campaigns`),\n form({\n name: String(name),\n objective: String(objective),\n status: \"PAUSED\",\n special_ad_categories: JSON.stringify(categories),\n }),\n \"Meta campaign creation\",\n );\n return ok({ applied: true, action: \"meta_create_campaign\", status: \"PAUSED\", result });\n },\n );\n\n // ── Budgets ───────────────────────────────────────────────────────\n server.tool(\n \"meta_update_adset_budget\",\n \"Change the daily or lifetime budget of a Meta ad set. Amount in the account currency \" +\n \"(12.50 for 12.50 EUR). Meta expects minor units, the conversion is done here. \" +\n \"Previews by default.\",\n {\n adSetId: z.string().describe(\"Ad set ID.\"),\n adAccountId: z.string().describe(\"Owning ad account, with or without the act_ prefix.\"),\n currency: z.string().length(3).describe(\"Account currency code, for example EUR. Read it with meta_list_ad_accounts.\"),\n dailyBudget: z.number().positive().optional().describe(\"Daily budget, in the account currency.\"),\n lifetimeBudget: z.number().positive().optional().describe(\"Lifetime budget, mutually exclusive with the daily budget.\"),\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const { adSetId, currency, dailyBudget, lifetimeBudget, confirm } = a;\n if (dailyBudget === undefined && lifetimeBudget === undefined) {\n return ko(\"Provide either dailyBudget or lifetimeBudget.\");\n }\n if (dailyBudget !== undefined && lifetimeBudget !== undefined) {\n return ko(\"dailyBudget and lifetimeBudget are mutually exclusive; Meta rejects both together.\");\n }\n const field = dailyBudget !== undefined ? \"daily_budget\" : \"lifetime_budget\";\n const amount = Number(dailyBudget ?? lifetimeBudget);\n let minor: number;\n try {\n minor = toMinorUnits(amount, String(currency));\n } catch (error) {\n return ko(error instanceof Error ? error.message : String(error));\n }\n if (!confirm) {\n return preview(\"meta_update_adset_budget\", {\n adSet: adSetId, field, amount, currency, inMinorUnits: minor,\n });\n }\n const result = await request(graph(String(adSetId)), form({ [field]: String(minor) }), \"Meta ad set budget\");\n return ok({ applied: true, action: \"meta_update_adset_budget\", result });\n },\n );\n\n server.tool(\n \"meta_update_campaign_budget\",\n \"Change the budget of a Meta campaign that holds its budget at campaign level (Advantage \" +\n \"campaign budget). On a campaign without one, Meta refuses and the ad set budget is the \" +\n \"one to change. Amount in the account currency. Previews by default.\",\n {\n campaignId: z.string().describe(\"Campaign ID.\"),\n adAccountId: z.string().describe(\"Owning ad account (act_… or bare ID).\"),\n currency: z.string().length(3).describe(\"Account currency code, for example EUR. Read it with meta_list_ad_accounts.\"),\n dailyBudget: z.number().positive().optional().describe(\"New daily budget, in the account currency.\"),\n lifetimeBudget: z.number().positive().optional().describe(\"New lifetime budget, in the account currency.\"),\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const { campaignId, currency, dailyBudget, lifetimeBudget, confirm } = a;\n if (dailyBudget === undefined && lifetimeBudget === undefined) {\n return ko(\"Provide either dailyBudget or lifetimeBudget.\");\n }\n if (dailyBudget !== undefined && lifetimeBudget !== undefined) {\n return ko(\"Provide only one of dailyBudget or lifetimeBudget; Meta holds one or the other.\");\n }\n const field = dailyBudget !== undefined ? \"daily_budget\" : \"lifetime_budget\";\n const amount = Number(dailyBudget ?? lifetimeBudget);\n let minor: number;\n try {\n minor = toMinorUnits(amount, String(currency));\n } catch (error) {\n return ko(error instanceof Error ? error.message : String(error));\n }\n if (!confirm) {\n return preview(\"meta_update_campaign_budget\", {\n campaign: campaignId, field, amount, currency, inMinorUnits: minor,\n });\n }\n const result = await request(graph(String(campaignId)), form({ [field]: String(minor) }), \"Meta campaign budget\");\n return ok({ applied: true, action: \"meta_update_campaign_budget\", result });\n },\n );\n\n // ── Schedule ──────────────────────────────────────────────────────\n server.tool(\n \"meta_update_adset_schedule\",\n \"Change the start or end time of a Meta ad set. Times are ISO 8601 with an offset, for \" +\n \"example 2026-09-01T00:00:00+0200. Previews by default.\",\n {\n adSetId: z.string().describe(\"Ad set ID.\"),\n adAccountId: z.string().describe(\"Owning ad account (act_… or bare ID).\"),\n startTime: z.string().optional().describe(\"Start time, ISO 8601 with an offset, for example 2026-09-01T00:00:00+0200.\"),\n endTime: z.string().optional().describe(\"End time, ISO 8601 with an offset, for example 2026-09-30T23:59:59+0200.\"),\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const { adSetId, startTime, endTime, confirm } = a as Record<string, string | boolean>;\n if (!startTime && !endTime) return ko(\"Provide startTime, endTime, or both.\");\n const fields: Record<string, string> = {};\n if (startTime) fields.start_time = String(startTime);\n if (endTime) fields.end_time = String(endTime);\n if (!confirm) return preview(\"meta_update_adset_schedule\", { adSet: adSetId, startTime, endTime });\n const result = await request(graph(String(adSetId)), form(fields), \"Meta ad set schedule\");\n return ok({ applied: true, action: \"meta_update_adset_schedule\", result });\n },\n );\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) 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 { MetaConfig } from \"../../config/types.js\";\nimport { registerMetaTools } from \"./tools.js\";\nimport { registerMetaResources } from \"./resources.js\";\nimport { registerMetaWrites } from \"./writes.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport function registerMeta(server: McpServer, config: MetaConfig): void {\n registerMetaTools(server, config);\n registerMetaResources(server, config.enableWrites ?? false);\n logger.info(\"meta\", \"Registered 34 read tools and 7 resources\");\n\n if (config.enableWrites) {\n registerMetaWrites(server, config);\n logger.info(\"meta\", \"Registered 10 write tools (every one previews before it applies)\");\n }\n}\n","/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { logger } from \"../core/logger.js\";\n\n/**\n * Connect the MCP server to stdio transport (for Claude Desktop, Claude Code, Cursor).\n */\nexport async function startStdioTransport(server: McpServer): Promise<void> {\n const transport = new StdioServerTransport();\n logger.system(\"Starting stdio transport...\");\n await server.connect(transport);\n logger.system(\"Stdio transport connected, waiting for MCP client\");\n}\n","#!/usr/bin/env node\n/**\n * meta-ads-mcp-server: an open-source MCP server for the Facebook (Meta) Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { loadConfig } from \"./config/index.js\";\nimport { createServer } from \"./server.js\";\nimport { startStdioTransport } from \"./transports/stdio.js\";\nimport { logger } from \"./core/logger.js\";\n\nasync function main(): Promise<void> {\n try {\n const config = loadConfig();\n logger.setLevel(config.logLevel);\n\n const server = createServer(config);\n await startStdioTransport(server);\n } catch (error) {\n logger.error(\"cli\", \"Fatal error\", error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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","names":["z","mergeResults","request","request","z","z","ok","preview","z","datePresetSchema","request","z","ok","z"]}
|