@getmcpads/meta-ads-mcp-server 1.0.1 → 2.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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server.ts","../src/platforms/meta/tools.ts","../src/core/logger.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/config/schema.ts","../src/config/index.ts","../src/transports/stdio.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 { 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 */\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 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 { 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 */\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 { 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"],"mappings":"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","names":["z","mergeResults","request","request","ok","preview","z","datePresetSchema","request","z","ok","z","z"]}
1
+ {"version":3,"sources":["../src/parameter-descriptions.ts","../src/tool-quality.ts","../src/server.ts","../src/platforms/meta/extended-writes.ts","../src/core/money.ts","../src/platforms/meta/reporting-context.ts","../src/platforms/meta/tools.ts","../src/core/logger.ts","../src/core/errors.ts","../src/core/rate-limiter.ts","../src/platforms/meta/client.ts","../src/platforms/meta/business-assets.ts","../src/platforms/meta/organic-insights.ts","../src/platforms/meta/creative-media.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/config/schema.ts","../src/config/index.ts","../src/transports/stdio.ts"],"sourcesContent":["/** Descriptions shared by equivalent parameters; explicit tool descriptions always win. */\nexport const PARAMETER_DESCRIPTIONS: Record<string, string> = {\n \"filters\": \"Filters combined according to this tool's query schema. Use provider field names and the declared operators.\",\n \"searchType\": \"Search surface to report, such as web, image, video, news or Discover. Availability depends on the property.\",\n \"searchTypes\": \"Search surfaces to compare. Each surface is queried separately; do not sum overlapping surfaces.\",\n \"datePreset\": \"Named reporting period. Use the custom date range when you need exact start and end dates.\",\n \"dateRange\": \"Explicit reporting start and end dates in YYYY-MM-DD format.\",\n \"timeRange\": \"Explicit provider reporting period with since and until dates.\",\n \"startDate\": \"First date of the reporting period, in YYYY-MM-DD format.\",\n \"endDate\": \"Last date of the reporting period, in YYYY-MM-DD format, on or after startDate.\",\n \"since\": \"Beginning of the requested reporting period; use the format accepted by the provider.\",\n \"until\": \"End of the requested reporting period; use the format accepted by the provider.\",\n \"orderBy\": \"Field used to order report rows.\",\n \"orderField\": \"Provider field used to order report rows.\",\n \"orderDirection\": \"Sort direction for the selected ordering field.\",\n \"orderType\": \"Sort direction for the selected ordering field.\",\n \"sortBy\": \"Metric or dimension used to rank the returned results.\",\n \"sort\": \"Native provider ordering expressions.\",\n \"limit\": \"Maximum number of returned items or rows. The declared bounds and default apply; use pagination for additional results.\",\n \"topN\": \"Maximum number of highest-ranked results to return.\",\n \"pageSize\": \"Maximum items requested per page. Follow the returned pagination information for remaining results.\",\n \"page\": \"Page number to request, starting at 1.\",\n \"offset\": \"Zero-based number of rows to skip before returning results.\",\n \"bookmark\": \"Opaque Pinterest pagination bookmark returned by the previous response. Omit on the first request.\",\n \"after\": \"Opaque cursor for the next page returned by Meta.\",\n \"before\": \"Opaque cursor for the previous page returned by Meta.\",\n \"includeUnchanged\": \"Include entries with no observed change in the comparison.\",\n \"minImpressions\": \"Minimum reported impressions required for a row to enter the analysis.\",\n \"objectives\": \"Analysis goals that determine the recommended large-site sampling strategy.\",\n \"pageLimit\": \"Maximum number of pages considered for the sampling plan.\",\n \"maxInspectionUrls\": \"Maximum URL Inspection candidates included in the plan; inspection calls consume provider quota.\",\n \"minImpressionsForLowCtr\": \"Minimum impressions before a page is considered for low-CTR analysis.\",\n \"staleSitemapDays\": \"Number of days after which a sitemap's last download is considered stale.\",\n \"continueOnError\": \"Continue with other URLs when one inspection fails, returning individual failures.\",\n \"dimension\": \"Reporting dimension used to group and compare results.\",\n \"maxClusters\": \"Maximum query clusters to return.\",\n \"topQueriesPerCluster\": \"Maximum example queries returned for each cluster.\",\n \"minPages\": \"Minimum distinct pages ranking for a query before it is considered a cannibalization candidate.\",\n \"adAccountId\": \"Owning advertising account ID. Use the exact ID returned by account discovery; do not substitute a campaign or business ID.\",\n \"advertiserId\": \"TikTok advertiser account ID as a string.\",\n \"customerId\": \"Google Ads customer ID, without hyphens.\",\n \"propertyId\": \"Google Analytics property ID; use the property, not the account or measurement ID.\",\n \"level\": \"Entity or aggregation level for the requested report.\",\n \"dataLevel\": \"TikTok reporting aggregation level. Metrics and dimensions must be compatible with this level.\",\n \"granularity\": \"Time bucket used to aggregate reporting rows.\",\n \"columns\": \"Native Pinterest reporting column names. Discover supported columns before selecting metrics.\",\n \"executionMode\": \"Choose the supported synchronous, asynchronous or automatic report execution strategy.\",\n \"entityIds\": \"Exact IDs of the entities to include in the selected reporting level.\",\n \"targetingTypes\": \"Native targeting categories used to break down delivery.\",\n \"clickWindowDays\": \"Click-through attribution window, in days.\",\n \"engagementWindowDays\": \"Engagement-through attribution window, in days.\",\n \"viewWindowDays\": \"View-through attribution window, in days.\",\n \"conversionReportTime\": \"Whether conversions are assigned to the ad action date or the conversion date.\",\n \"attributionTypes\": \"Provider attribution categories included in the report.\",\n \"reportingTimezone\": \"Provider reporting timezone selector. Keep it consistent when comparing periods.\",\n \"campaignIds\": \"Restrict results to these campaign IDs.\",\n \"adGroupIds\": \"Restrict results to these ad group IDs.\",\n \"adgroupIds\": \"Restrict TikTok results to these ad group IDs.\",\n \"adIds\": \"Restrict results to these ad IDs.\",\n \"campaignId\": \"Exact campaign ID in the selected advertising account.\",\n \"adGroupId\": \"Exact ad group ID in the selected advertising account.\",\n \"adSetId\": \"Exact Meta ad set ID in the selected advertising account.\",\n \"adId\": \"Exact ad ID in the selected advertising account.\",\n \"entityId\": \"Exact ID of the entity selected by this operation. It must belong to the selected account.\",\n \"onlyWithPeriodDelivery\": \"Keep only creatives with observed delivery in the requested date range.\",\n \"breakdown\": \"Native reporting breakdown used to group results.\",\n \"breakdowns\": \"Native reporting breakdowns. Check compatibility before combining them.\",\n \"productGroupIds\": \"Restrict reporting to these product group IDs.\",\n \"productItemIds\": \"Restrict reporting to these product item IDs.\",\n \"includeProductSamples\": \"Include a limited sample of product records alongside inventory diagnostics.\",\n \"sampleProductGroups\": \"Maximum product groups to sample for inventory diagnostics.\",\n \"reportName\": \"Provider report name identifying the requested report recipe.\",\n \"conversionProductAttributionType\": \"Pinterest product-conversion attribution category.\",\n \"conversionProductBreakdown\": \"Pinterest product-conversion grouping level.\",\n \"productSkuIds\": \"Restrict product-conversion reporting to these SKU IDs.\",\n \"request\": \"Exact request schema name to inspect before composing a write payload.\",\n \"entity\": \"Entity collection or level to inspect.\",\n \"entityType\": \"Provider entity type to retrieve.\",\n \"entityStatuses\": \"Native status values used to filter the entity collection.\",\n \"statusFilter\": \"Native status values used to filter the returned entities.\",\n \"targetingType\": \"Targeting dictionary to inspect, such as interests, locations or demographics.\",\n \"interestId\": \"Native interest ID for the targeting lookup.\",\n \"mode\": \"Operation or analysis mode. Only the values declared in this schema are supported.\",\n \"matchTypes\": \"Keyword match types included in the lookup.\",\n \"countryCode\": \"Country code used to localize keyword or targeting results.\",\n \"keywords\": \"Keyword phrases to analyze; use the language and market of the intended audience.\",\n \"term\": \"Search term used for the keyword lookup.\",\n \"terms\": \"Search terms used for the keyword lookup.\",\n \"audienceId\": \"Exact ID of the audience to inspect or modify.\",\n \"customerListId\": \"Exact customer-list ID. Reading metadata does not upload customer data.\",\n \"businessId\": \"Exact business account ID used for the lookup.\",\n \"accountType\": \"Provider account category used to filter results.\",\n \"ownershipType\": \"Filter audiences by provider ownership category.\",\n \"excludeNca\": \"Whether to exclude Pinterest new-customer-acquisition audiences from this lookup.\",\n \"audienceInsightType\": \"Pinterest audience-insights category to request.\",\n \"surfaces\": \"Conversion-setup surfaces to inspect, according to the supported values.\",\n \"includeDeletedTags\": \"Include deleted conversion tags in diagnostics.\",\n \"lookbackPeriod\": \"Provider lookback period used for conversion diagnostics.\",\n \"sourcePlatform\": \"Filter conversion data by source platform.\",\n \"ingestionSource\": \"Filter conversion events by ingestion source.\",\n \"catalogId\": \"Exact product catalog ID accessible to the selected account.\",\n \"feedId\": \"Exact catalog feed ID.\",\n \"processingResultId\": \"Exact catalog feed processing result ID to inspect.\",\n \"productGroupId\": \"Exact product group ID.\",\n \"productSetId\": \"Exact Meta product set ID belonging to the selected catalog.\",\n \"exportType\": \"Provider export category to request.\",\n \"action\": \"Supported operation within this tool's scoped provider surface.\",\n \"includeDetails\": \"Include detailed provider records in addition to summary information.\",\n \"leadFormId\": \"Exact lead form ID to inspect.\",\n \"subscriptionId\": \"Exact subscription ID to inspect.\",\n \"assetId\": \"Exact provider asset ID.\",\n \"memberId\": \"Exact business member ID used for the read lookup.\",\n \"partnerId\": \"Exact business partner ID used for the read lookup.\",\n \"pinId\": \"Exact Pinterest pin ID.\",\n \"pinIds\": \"Exact Pinterest pin IDs to include.\",\n \"metrics\": \"Native or documented calculated metric names to request. Check compatibility with the selected dimensions.\",\n \"metric\": \"Metric used for the analysis; use a name from the platform metric catalogue.\",\n \"boardId\": \"Exact Pinterest board ID.\",\n \"searchTerm\": \"Text used to filter or search the selected collection.\",\n \"region\": \"Provider region selector used to localize trends.\",\n \"productRegion\": \"Region used for product trend analysis.\",\n \"trendType\": \"Provider trend category to return.\",\n \"interests\": \"Native interest categories used to filter trends.\",\n \"genders\": \"Native gender categories used to filter aggregate trends.\",\n \"ageBuckets\": \"Native age groups used to filter aggregate trends.\",\n \"includeKeywords\": \"Include related keyword details in the trend response.\",\n \"includeDemographics\": \"Include available aggregate demographic breakdowns.\",\n \"productCategories\": \"Native product category IDs or values used to filter trends.\",\n \"verticals\": \"Native business verticals used to filter trends.\",\n \"productLookbackDays\": \"Product-trend lookback window, in days.\",\n \"productEngagementType\": \"Engagement signal used for product-trend analysis.\",\n \"featuredInterest\": \"Featured interest category for the trends lookup.\",\n \"reportType\": \"Native provider report type; determines supported metrics and dimensions.\",\n \"promotionId\": \"Exact Pinterest product-group promotion ID.\",\n \"type\": \"Provider object or operation type selected from this schema's allowed values.\",\n \"productBreakdown\": \"Native product grouping used to join catalog items with reported insights.\",\n \"catalogProductLimit\": \"Maximum catalog product records read for the join.\",\n \"includeAdsets\": \"Include ad set configuration when checking brand safety.\",\n \"includeBlockLists\": \"Include accessible brand-safety block-list metadata.\",\n \"includeRawTargeting\": \"Include the native targeting specification for diagnostics.\",\n \"cellEntityType\": \"Entity type represented by experiment cells.\",\n \"includePagePosts\": \"Read related Facebook Page posts when the token has permission.\",\n \"includeInstagramMedia\": \"Read related Instagram media when the token has permission.\",\n \"includeInsights\": \"Include available insight metrics; additional provider permissions may be required.\",\n \"edge\": \"Allowlisted Meta Graph edge to read on the selected node.\",\n \"fields\": \"Native provider fields to return. Select only fields supported by the chosen object or report.\",\n \"filtering\": \"Native provider filter expressions; credentials and account overrides are not allowed.\",\n \"includeSummary\": \"Request the provider's available summary or total-count metadata.\",\n \"actionBreakdowns\": \"Meta action-level breakdowns, subject to Insights compatibility rules.\",\n \"actionAttributionWindows\": \"Meta attribution windows used to attribute action metrics.\",\n \"timeIncrement\": \"Provider time-bucket size or supported aggregate value.\",\n \"locationTypes\": \"Location dictionary categories to include in targeting search.\",\n \"adFormat\": \"Native Meta ad preview format; must be compatible with this ad's creative.\",\n \"handle\": \"Asynchronous catalog batch handle returned by the submission to inspect.\",\n \"keepEmptyRows\": \"Keep provider rows whose selected metrics are all zero, where supported.\",\n \"sampleLimit\": \"Maximum records included in the diagnostic sample.\",\n \"isOpenFunnel\": \"Allow users to enter at any funnel step instead of requiring the first step.\",\n \"visualizationType\": \"Supported funnel visualization mode.\",\n \"breakdownLimit\": \"Maximum breakdown values to include in the funnel response.\",\n \"nextActionLimit\": \"Maximum next actions returned for each funnel step.\",\n \"fallbackToStepCounts\": \"If the advanced funnel is unavailable, return independent step counts with the limitation made explicit.\",\n \"report\": \"Native GA4 report request object for this endpoint.\",\n \"reports\": \"Native GA4 report request objects to execute in one batch.\",\n \"dimensions\": \"Native reporting dimensions. Validate compatibility with the selected metrics.\",\n \"compatibilityFilter\": \"Limit compatibility results to the requested GA4 compatibility category.\",\n \"collection\": \"Allowlisted administrative resource collection to read.\",\n \"settings\": \"Property configuration surfaces to inspect.\",\n \"includeRecurring\": \"Include recurring audience-export configurations.\",\n \"audienceExportName\": \"Full GA4 audience-export resource name returned by list_audience_exports.\",\n \"queryLifetime\": \"Request lifetime reporting instead of the explicit reporting period where TikTok supports it.\",\n \"declineThresholdPct\": \"Percentage decline used as a candidate signal for creative fatigue, requiring further evidence.\",\n \"includeSavedAudiences\": \"Include accessible saved-audience metadata in the overlap analysis.\",\n \"overlapThreshold\": \"Overlap threshold used to flag audience pairs, in the units declared by this schema.\",\n \"serviceType\": \"TikTok report service category.\",\n \"catalog\": \"Allowlisted targeting dictionary or catalog to inspect.\",\n \"query\": \"Search text used to find matching entries in the selected catalogue.\",\n \"parameters\": \"Native query parameters for this allowlisted read endpoint. Do not provide credentials or account overrides.\",\n \"account_id\": \"Exact account ID selected for this platform in the GetMCPAds workspace.\",\n \"platform\": \"Platform key identifying the connected advertising or analytics source.\",\n \"id\": \"Exact saved object ID returned by the corresponding create or list tool.\",\n \"revision\": \"Last known object revision, used to reject conflicting updates. Read the object before editing.\",\n \"name\": \"Human-readable name for this object.\",\n \"title\": \"Human-readable title for the resulting object or review.\",\n \"clientLabel\": \"Display label identifying the client in the saved view.\",\n \"config\": \"Complete saved-view configuration, including source, selected account, period and display options.\",\n \"subject\": \"Entity or creative subject whose dated performance will be analyzed.\",\n \"compare_previous\": \"Include the immediately preceding period of equal duration for comparison.\",\n \"profile\": \"Creative brief profile containing the client's business context and analysis preferences.\",\n \"primaryMetric\": \"Metric used to rank or evaluate the review; it must be available for the selected platform.\",\n \"direction\": \"Whether higher or lower values of the primary metric represent improvement.\",\n \"creative_ids\": \"Exact creative IDs to include in this review.\",\n \"request_id\": \"Caller-generated request identifier. Reuse it for a retry of the same operation to avoid duplicate work.\",\n \"expiresInDays\": \"Number of days before the public review sharing link expires.\",\n \"client_id\": \"Exact saved business-client workspace ID.\",\n \"review_id\": \"Exact review ID returned by the review creation or listing tool.\",\n \"page_id\": \"Exact page identifier within the landing-page review.\",\n \"entity_id\": \"Exact entity identifier in the selected review or platform context.\",\n \"source_key\": \"Source identifier returned by the business review; keep the platform/account association intact.\",\n \"metric_key\": \"Exact metric key in the review evidence.\",\n \"follow_up_on\": \"Follow-up date for the decision, using the format declared in this schema.\",\n \"hypothesis\": \"Specific hypothesis to record, supported by the review evidence.\",\n \"successCriterion\": \"Measurable condition used to decide whether the recorded action succeeded.\",\n \"outcome\": \"Observed outcome to record; distinguish evidence from an untested expectation.\",\n \"status\": \"Status selected from the allowed values for this object or operation.\",\n \"retry_source\": \"Source identifier to retry after reviewing its reported error.\",\n \"site_origin\": \"Website origin to analyze, including https:// and hostname, without credentials or a path.\",\n \"matchType\": \"Google Ads keyword match type for the forecast.\",\n \"negativeKeywords\": \"Keyword phrases excluded from the keyword forecast.\",\n \"negativeMatchType\": \"Match type applied to the negative keywords.\",\n \"geoTargetIds\": \"Google Ads geo-target constant IDs for the intended locations.\",\n \"languageId\": \"Google Ads language constant ID for the keyword request.\",\n \"languageIds\": \"Google Ads language constant IDs for the intended audience.\",\n \"network\": \"Google Ads search network selector used for planning.\",\n \"biddingStrategy\": \"Supported bidding strategy for this temporary forecast. The request does not create a live campaign.\",\n \"includeAdultKeywords\": \"Allow adult keyword ideas where supported by the provider and the selected market.\",\n \"seedKeywords\": \"Seed keyword phrases from which Google generates keyword ideas.\",\n \"includeAverageCpc\": \"Request average cost-per-click metrics when available.\",\n \"includeDeviceBreakdown\": \"Include device-specific planning metrics when supported.\",\n \"includeKeywordConcepts\": \"Include Google's keyword concept grouping metadata.\",\n \"startYearMonth\": \"First year and month of the historical metrics period.\",\n \"endYearMonth\": \"Last year and month of the historical metrics period.\",\n \"includeDismissed\": \"Include recommendations already dismissed in the account.\",\n \"operation\": \"Exact allowlisted operation to execute. Arbitrary provider operations are not accepted.\",\n \"category\": \"Category selected from the provider dictionary or field catalogue.\",\n \"selectable\": \"Filter the field catalogue by whether the field can appear in SELECT.\",\n \"filterable\": \"Filter the field catalogue by whether the field can appear in WHERE.\",\n \"sortable\": \"Filter the field catalogue by whether the field can appear in ORDER BY.\",\n \"pageToken\": \"Opaque continuation token returned by the preceding provider response.\",\n \"locationNames\": \"Human-readable location names for Google to resolve into geo-target constants.\",\n \"locale\": \"Locale used for human-readable lookup results.\",\n \"resource\": \"Native Google Ads resource from which the GAQL query selects rows.\",\n \"cursor\": \"Opaque pagination cursor returned by the preceding response; omit for the first page.\",\n \"kind\": \"Entity or request kind whose schema or data is being requested.\",\n \"startTime\": \"Beginning of the reporting period as an ISO 8601 timestamp with an explicit timezone.\",\n \"endTime\": \"End of the reporting period as an ISO 8601 timestamp with an explicit timezone, after startTime.\",\n \"reportDimension\": \"Native dimension used to break down the Snapchat report.\"\n};\n\nexport function describeInputSchema<T extends Record<string, any>>(schema:T):T {\n return { ...schema, ...(schema.properties ? {properties:Object.fromEntries(Object.entries(schema.properties).map(([key,value]:[string,any]) => [key,{...value,...(!value.description && PARAMETER_DESCRIPTIONS[key] ? {description:PARAMETER_DESCRIPTIONS[key]} : {})}]))} : {}) };\n}\n","import { PARAMETER_DESCRIPTIONS } from \"./parameter-descriptions.js\";\n/** Copyright 2026 GetMCPAds. SPDX-License-Identifier: Apache-2.0 */\n/** A stable structured envelope for the heterogeneous provider responses.\n * The original MCP content remains unchanged. Provider\n * fields deliberately remain open: native query tools accept selected fields.\n */\nexport const RESULT_SCHEMA = {\n type: \"object\" as const,\n properties: {\n result: {\n description: \"Original tool result: parsed JSON when the text is JSON, otherwise the text or multiple MCP content blocks. Provider fields depend on the selected query.\",\n type: [\"object\", \"array\", \"string\", \"number\", \"boolean\", \"null\"],\n },\n },\n required: [\"result\"],\n additionalProperties: true,\n};\n\nexport type ToolAnnotations = {\n title?: string;\n readOnlyHint: boolean;\n destructiveHint: boolean;\n idempotentHint: boolean;\n openWorldHint: boolean;\n};\n\nexport function toolAnnotations(write: boolean): ToolAnnotations {\n // Conservative for writes: an update can replace existing data, and a\n // creation or upload must never be blindly retried after an uncertain reply.\n return { readOnlyHint: !write, destructiveHint: write, idempotentHint: !write, openWorldHint: true };\n}\n\nexport function structuredResult<T extends { content: unknown[]; isError?: boolean; structuredContent?: Record<string, unknown> }>(value: T): T {\n if (value.isError) return value;\n let result: unknown = value.content;\n const first = value.content[0] as { type?: string; text?: string } | undefined;\n if (value.content.length === 1 && first?.type === \"text\" && typeof first.text === \"string\") {\n result = first.text;\n try { result = JSON.parse(first.text); } catch { /* Plain text is valid. */ }\n }\n return { ...value, structuredContent: { ...value.structuredContent, result } };\n}\n\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nexport type ToolShape = Record<string, z.ZodTypeAny>;\nconst resultShape = { result: z.union([z.object({}).passthrough(), z.array(z.unknown()), z.string(), z.number(), z.boolean(), z.null()]).describe(RESULT_SCHEMA.properties.result.description) };\n\n/** Adapt legacy tool registrations through the public SDK API. No credentials\n * or tool execution are required to construct the metadata catalogue. */\nexport function installToolQuality(server: McpServer): void {\n server.tool = ((name: string, description: string, inputSchema: ToolShape, handler: (...args: any[]) => any) => {\n const write = Object.hasOwn(inputSchema, \"confirm\");\n inputSchema = Object.fromEntries(Object.entries(inputSchema).map(([key, schema]) => [key, schema.description || PARAMETER_DESCRIPTIONS[key] ? schema.describe(schema.description || PARAMETER_DESCRIPTIONS[key]) : schema]));\n return server.registerTool(name, {\n title: name.replace(/_/g, \" \"), description, inputSchema,\n outputSchema: resultShape, annotations: toolAnnotations(write),\n }, async (...args: any[]) => structuredResult(await handler(...args)));\n }) as typeof server.tool;\n}\n","import { installToolQuality } from \"./tool-quality.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 = \"2.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 title: \"Meta Ads\",\n websiteUrl: \"https://www.getmcpads.com/tools/meta-ads\",\n icons: [{ src: \"https://mcp.getmcpads.com/icon.svg\", mimeType: \"image/svg+xml\" }],\n },\n {\n capabilities: {\n tools: { listChanged: true },\n resources: { subscribe: false, listChanged: true },\n },\n },\n );\n\n installToolQuality(server);\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","/** Copyright 2026 getmcpads. SPDX-License-Identifier: Apache-2.0 */\nimport {z} from 'zod';\nimport type {ToolShape} from '../../tool-quality.js';\nimport {toMinorUnits} from '../../core/money.js';\ntype Row=Record<string,any>;\ntype Collector={tool:(name:string,description:string,shape:ToolShape,handler:(args:Row)=>Promise<unknown>)=>void};\n// Discover larger write schemas on demand; keep tools/list compact for every host.\nexport const DEFERRED_META_WRITES = new Set([\n 'meta_create_campaign',\n 'meta_create_adset','meta_update_adset_configuration','meta_duplicate_adset',\n 'meta_create_adcreative','meta_create_ad','meta_update_ad_creative',\n 'meta_upload_ad_image','meta_upload_ad_video','meta_create_custom_audience',\n 'meta_update_custom_audience','meta_create_product_set','meta_update_product_set','meta_batch_catalog_items',\n]);\nconst parameterDescriptions:Record<string,string>={\n adAccountId:'Selected owning ad account ID, with or without act_. Every referenced ad object must belong to it.',\n adSetId:'Numeric ID of the existing ad set in the selected account.',\n campaignId:'Numeric ID of the parent campaign in the selected account. Its budget model and objective determine compatible ad set settings.',\n creativeId:'Numeric ID of an existing ad creative in the selected account; not an image hash or video ID.',\n adId:'Numeric ID of the existing ad to modify in the selected account.',\n name:'Exact business name to give this object. Do not derive it from an uploaded filename.',\n title:'Optional title for the uploaded video.',\n configuration:'Explicit native Meta ad set settings. First read meta_get_adset_configuration for edits. A supplied targeting object replaces the entire targeting spec.',\n dailyBudget:'Daily budget in major account-currency units, e.g. 10 means EUR 10. Cannot be combined with lifetimeBudget or a campaign-owned budget.',\n lifetimeBudget:'Total lifetime budget in major account-currency units. Requires an end time; mutually exclusive with dailyBudget.',\n bidAmount:'Bid amount in major account-currency units. Only use with a compatible capped bidding strategy.',\n spec:'Native Meta creative specification: exactly one existing post ID or an object_story_spec with page_id. Put image/video/carousel content in that spec; asset_feed_spec is optional.',\n trackingSpecs:'Optional native Meta tracking specifications. Do not supply credentials.',\n bytesBase64:'Base64 media bytes without a data-URL prefix. Maximum decoded size 5 MiB; omitted from preview output.',\n fileUrl:'Publicly reachable HTTPS video file URL. Must point to the media file, not a landing page or a logged-in player.',\n subtype:'Audience type. CUSTOM creates an empty customer-list container; WEBSITE and ENGAGEMENT require a rule; LOOKALIKE requires an origin and lookalikeSpec.',\n description:'Description of the audience purpose.',rule:'Complete native Meta audience rule. Replaces the existing rule on update.',\n retentionDays:'Audience retention in days, from 1 to 180; Meta enforces eligibility for the selected subtype.',\n pixelId:'Numeric ID of the pixel used by a website audience rule.',originAudienceId:'Numeric ID of the source audience in this same ad account.',\n lookalikeSpec:'Native lookalike specification, including explicit geography and size. Subject to Meta eligibility.',\n customerFileSource:'Actual provenance of future customer-list data. Creating the container does not upload customer data.',\n audienceId:'Numeric ID of the existing custom audience in this selected ad account.',\n catalogId:'Numeric catalog ID. Writes require the catalog and selected ad account to have the same owning Business.',\n productSetId:'Numeric product set ID belonging to catalogId.',filter:'Complete native product-set filter. Updates replace the existing filter.',\n requests:'One to 50 product CREATE or UPDATE requests with retailer_id and native product data. No DELETE; inspect the asynchronous batch result.',\n startTime:'Optional new start time as ISO 8601 with explicit UTC offset.',endTime:'Optional new end time as ISO 8601 with explicit UTC offset, after the start.',\n};\nconst id=z.string().regex(/^\\d+$/,'Use a numeric Meta ID');\nconst accountId=z.string().regex(/^(act_)?\\d+$/);\nconst json=z.record(z.unknown());\nconst name=z.string().trim().min(1).max(400);\nconst date=z.string().datetime({offset:true});\nconst amount=z.number().positive().max(1_000_000);\nconst currency=z.string().regex(/^[A-Z]{3}$/);\nconst mode={confirm:z.boolean().optional().describe('Apply only when true. Default is a local preview with no API mutation.'),validateOnly:z.boolean().optional().describe('Ask Meta to validate, without creating or changing an object. Mutually exclusive with confirm.')};\nconst money={currency:currency.optional().describe('Required with any monetary amount. Must match the actual account currency; never assume EUR.'),dailyBudget:amount.optional(),lifetimeBudget:amount.optional(),bidAmount:amount.optional()};\nconst adsetConfig=z.object({\n optimization_goal:z.string().min(1).optional(),billing_event:z.string().min(1).optional(),\n targeting:json.optional().describe('Complete Meta targeting spec: geography, demographics, custom audiences, exclusions, placements and targeting_automation. Updates replace the entire targeting spec.'),\n promoted_object:json.optional(),destination_type:z.string().optional(),\n bid_strategy:z.enum(['LOWEST_COST_WITHOUT_CAP','LOWEST_COST_WITH_BID_CAP','COST_CAP','LOWEST_COST_WITH_MIN_ROAS']).optional(),\n bid_constraints:json.optional(),start_time:date.optional(),end_time:date.optional(),\n attribution_spec:z.array(json).max(10).optional(),attribution_count_type:z.string().optional(),\n dsa_beneficiary:name.optional(),dsa_payor:name.optional(),\n is_dynamic_creative:z.boolean().optional(),pacing_type:z.array(z.string()).optional(),\n adset_schedule:z.array(json).optional(),frequency_control_specs:z.array(json).optional(),\n optimization_sub_event:z.string().optional(),is_incremental_attribution_enabled:z.boolean().optional(),\n}).strict();\nconst creativeSpec=z.object({\n object_story_id:z.string().regex(/^\\d+_\\d+$/).optional(),object_story_spec:json.optional(),\n asset_feed_spec:json.optional(),instagram_user_id:id.optional(),product_set_id:id.optional(),\n degrees_of_freedom_spec:json.optional(),url_tags:z.string().max(4000).optional(),\n contextual_multi_ads:json.optional(),dynamic_ad_voice:z.string().optional(),\n image_crops:json.optional(),template_url:z.string().url().optional(),template_url_spec:json.optional(),\n}).strict();\nconst out=(data:unknown,isError=false)=>({...(isError?{isError:true}:{}),content:[{type:'text' as const,text:JSON.stringify(data,null,2)}]});\nconst act=(v:string)=>v.replace(/^act_/,'');\nconst obj=(v:unknown):Row=>v&&typeof v==='object'&&!Array.isArray(v)?v as Row:{};\nconst has=(v:unknown)=>v!==undefined&&v!==null;\nfunction clean(v:Row):Row{return Object.fromEntries(Object.entries(v).filter(([,value])=>value!==undefined));}\n// Meta can silently replace collection media with the linked Instant Experience's\n// hero. Preserve the successful write ID, but stop dependent ads on any mismatch.\nfunction mediaDifferences(requested:unknown,returned:unknown,path=''):Row[]{\n if(!requested||typeof requested!=='object')return [];\n return Object.entries(requested).flatMap(([key,value])=>{\n const field=path?`${path}.${key}`:key,actual=(returned as Row|undefined)?.[key];\n if((['image_hash','video_id','photo_id'].includes(key)||(key==='hash'&&path.startsWith('asset_feed_spec.images.')))&&value!==actual)return [{field,requested:value,returned:actual??null}];\n return mediaDifferences(value,actual,field);\n });\n}\nfunction safeJson(value:unknown,depth=0):void {\n if(depth>15)throw new Error('Specification is too deeply nested.');\n if(value&&typeof value==='object')for(const [k,v] of Object.entries(value)){\n if(['access_token','appsecret_proof','__proto__','constructor','prototype'].includes(k))throw new Error(`Forbidden specification key: ${k}`);\n safeJson(v,depth+1);\n }\n}\nfunction validateAdsetState(p:Row):void {\n if(p.start_time&&p.end_time&&Date.parse(p.end_time)<=Date.parse(p.start_time))throw new Error('end_time must be after start_time.');\n if(Number(p.lifetime_budget)>0&&!p.end_time)throw new Error('A lifetime budget requires end_time.');\n if(p.bid_strategy==='LOWEST_COST_WITHOUT_CAP'&&Number(p.bid_amount)>0)throw new Error('Automatic lowest-cost bidding cannot include a bid cap.');\n if(['COST_CAP','LOWEST_COST_WITH_BID_CAP'].includes(p.bid_strategy)&&!(Number(p.bid_amount)>0))throw new Error('This bidding strategy requires bidAmount.');\n if(p.bid_strategy==='LOWEST_COST_WITH_MIN_ROAS'&&!(Number(obj(p.bid_constraints).roas_average_floor)>0))throw new Error('Minimum ROAS bidding requires bid_constraints.roas_average_floor.');\n}\nexport function buildAdsetPayload(a:Row,creating:boolean):Row {\n const config=adsetConfig.parse(a.configuration??{});safeJson(config);\n if(has(a.dailyBudget)&&has(a.lifetimeBudget))throw new Error('Choose dailyBudget OR lifetimeBudget, not both.');\n if([a.dailyBudget,a.lifetimeBudget,a.bidAmount].some(has)&&!a.currency)throw new Error('Supply the account currency for monetary amounts.');\n const p:Row={...config};\n for(const [from,to] of [['dailyBudget','daily_budget'],['lifetimeBudget','lifetime_budget'],['bidAmount','bid_amount']])if(has(a[from])){\n p[to]=toMinorUnits(Number(a[from]),String(a.currency));if(p[to]<1)throw new Error(`${from} rounds to zero in ${a.currency}.`);\n }\n if(creating){p.campaign_id=a.campaignId;p.name=a.name;p.status='PAUSED';\n for(const field of ['optimization_goal','billing_event','targeting'])if(!p[field])throw new Error(`${field} is required to create an ad set.`);\n }\n if(p.start_time&&p.end_time&&Date.parse(p.end_time)<=Date.parse(p.start_time))throw new Error('end_time must be after start_time.');\n if(creating&&p.lifetime_budget&&!p.end_time)throw new Error('A lifetime budget requires end_time.');\n const targeting=obj(p.targeting);\n if(obj(p.promoted_object).custom_conversion_id&&obj(p.promoted_object).pixel_id)throw new Error('For a custom conversion use custom_conversion_id without pixel_id. Meta resolves the pixel through the conversion.');\n if(p.targeting){\n for(const key of ['age_min','age_max'])if(has(targeting[key])&&(!Number.isInteger(targeting[key])||Number(targeting[key])<13||Number(targeting[key])>65))throw new Error(`${key} must be an integer from 13 to 65 (65 means 65+).`);\n if(targeting.locales&&(!Array.isArray(targeting.locales)||targeting.locales.some((v:unknown)=>!Number.isInteger(v)||Number(v)<=0)))throw new Error('Use native numeric locale IDs.');\n const geo=obj(targeting.geo_locations);\n if(geo.countries&&(!Array.isArray(geo.countries)||!geo.countries.length||geo.countries.some((v:unknown)=>typeof v!=='string'||!/^([A-Z]{2})$/.test(v))))throw new Error('countries must contain explicit two-letter uppercase country codes.');\n if(!Object.entries(geo).some(([key,v])=>key!=='location_types'&&Array.isArray(v)&&v.length))throw new Error('Specify nonempty geo_locations explicitly.');\n if(obj(targeting.exclusions).custom_audiences)throw new Error('Use targeting.excluded_custom_audiences for audience exclusions; exclusions.custom_audiences is not supported by Meta.');\n const excluded=new Set((targeting.excluded_custom_audiences??[]).map((v:Row)=>v.id));\n if((targeting.custom_audiences??[]).some((v:Row)=>excluded.has(v.id)))throw new Error('The same custom audience cannot be included and excluded.');\n }\n if(targeting.age_min&&targeting.age_max&&Number(targeting.age_min)>Number(targeting.age_max))throw new Error('Targeting age_min exceeds age_max.');\n if(targeting.exclusions&&(obj(targeting.exclusions).interests||obj(targeting.exclusions).behaviors||obj(targeting.exclusions).demographics))throw new Error('Meta no longer supports detailed-targeting exclusions. Use supported audience or geographic exclusions.');\n if(creating&&!Object.keys(obj(targeting.geo_locations)).length)throw new Error('Specify geo_locations explicitly; no country is assumed.');\n if(p.bid_strategy==='LOWEST_COST_WITHOUT_CAP'&&p.bid_amount)throw new Error('Automatic lowest-cost bidding cannot include a bid cap.');\n if(creating&&['COST_CAP','LOWEST_COST_WITH_BID_CAP'].includes(p.bid_strategy)&&!p.bid_amount)throw new Error('This bidding strategy requires bidAmount.');\n if(creating&&p.bid_strategy==='LOWEST_COST_WITH_MIN_ROAS'&&!obj(p.bid_constraints).roas_average_floor)throw new Error('Minimum ROAS bidding requires bid_constraints.roas_average_floor.');\n if(creating)validateAdsetState(p);\n if(!creating&&!Object.keys(p).length)throw new Error('No ad set change was supplied.');\n return p;\n}\n\n/** One account-bound native mutation per call. Never accepts arbitrary Graph paths. */\nexport function registerMetaExtendedWrites(c:Collector,config:Record<string,string>,readOnly=false):void {\n const base=`https://graph.facebook.com/${config.apiVersion||'v26.0'}`;\n async function graph(path:string,method:'GET'|'POST',data:Row={},file?:{base64:string,name:string}):Promise<Row>{\n const headers={authorization:`Bearer ${config.accessToken}`};\n const query=new URLSearchParams();for(const [k,v] of Object.entries(clean(data)))query.set(k,typeof v==='object'?JSON.stringify(v):String(v));\n let upload:FormData|undefined;\n if(file){upload=new FormData();for(const [k,v] of query)upload.set(k,v);upload.set('source',new Blob([Uint8Array.from(atob(file.base64),ch=>ch.charCodeAt(0))],{type:'video/mp4'}),file.name);}\n let response:Response;\n try{response=await fetch(`${base}/${path}${method==='GET'?'?'+query:''}`,{method,headers:method==='GET'||file?headers:{...headers,'content-type':'application/x-www-form-urlencoded'},...(method==='POST'?{body:upload??query}:{}),signal:AbortSignal.timeout(45000),redirect:\"error\"});}\n catch{throw Object.assign(new Error(method==='POST'?'Meta did not confirm the mutation. Check the account before retrying; a duplicate may otherwise be created.':'Meta could not be reached.'),{outcome:method==='POST'?'unknown':'not_applied'});}\n let dataOut:Row;try{dataOut=await response.json() as Row;}catch{throw Object.assign(new Error('Meta returned an unreadable response. Check the object before retrying.'),{outcome:method==='POST'?'unknown':'not_applied'});}\n if(!response.ok||dataOut.error){const e=obj(dataOut.error);throw Object.assign(new Error(String(e.error_user_msg||e.message||`Meta HTTP ${response.status}`).replaceAll(config.accessToken,'[redacted]').slice(0,700)),{code:e.code,subcode:e.error_subcode,outcome:'not_applied'});}\n return dataOut;\n }\n async function owned(nodeId:string,account:string,fields='id,account_id'):Promise<Row>{\n const value=await graph(nodeId,'GET',{fields});\n if(String(value.account_id)!==act(account))throw new Error('The target does not belong to the selected ad account. Nothing was changed.');\n return value;\n }\n async function account(account:string,includeBusiness=false){return graph(`act_${act(account)}`,'GET',{fields:includeBusiness?'id,currency,business{id}':'id,currency'});}\n async function catalog(catalogId:string,adAccountId:string){\n const [a,p]=await Promise.all([account(adAccountId,true),graph(catalogId,'GET',{fields:'id,business{id}'})]);\n if(!a.business?.id||String(a.business.id)!==String(p.business?.id))throw new Error('Catalog writes require a catalog owned by the same Business as this selected ad account. Shared catalogs are not writable through this tool.');\n }\n if(readOnly){\n const read=(n:string,d:string,shape:ToolShape,fn:(a:Row)=>Promise<unknown>)=>{const schema=z.object({adAccountId:accountId,...shape}).strict();c.tool(n,d,schema.shape,async raw=>{try{return out(await fn(schema.parse(raw)));}catch(e){return out({error:(e as Error).message},true);}});};\n read('meta_get_entity_configuration','Read a specific entity in its ad account for reconciliation. Read-only; never creates, changes or retries the entity. Missing or normalized fields must be inspected before claiming an exact match.',{\n entityId:id.describe('Exact entity ID saved in the acknowledged creation receipt.'),\n fields:z.array(z.enum(['name','status','objective','daily_budget','lifetime_budget','special_ad_categories','special_ad_category_country','bid_strategy','campaign_id','optimization_goal','billing_event','targeting','promoted_object','bid_amount','bid_constraints','start_time','end_time','attribution_spec','destination_type','is_dynamic_creative','dsa_beneficiary','dsa_payor','adset_id','creative','tracking_specs','conversion_domain','object_story_spec','asset_feed_spec','object_story_id','instagram_actor_id','instagram_user_id','url_tags','degrees_of_freedom_spec','is_adset_budget_sharing_enabled'])).min(1).max(40).describe('Exact fields to re-read from the reviewed native creation payload. IDs and account ownership are always checked.'),\n },async a=>({entity:await owned(a.entityId,a.adAccountId,['id','account_id',...a.fields].join(','))}));\n read('meta_get_uploaded_video','Check video processing and membership in the selected account library. If the first page misses the ID, use the video title to narrow the account-scoped lookup. A direct video read alone never proves membership or readiness. Bounded pagination; not_found is not success.',{videoId:id.describe('Exact uploaded video ID returned by Meta in this account.')},async a=>{\n let after:string|undefined,title:string|undefined;\n for(let page=0;page<6;page++){\n const result=await graph(`act_${act(a.adAccountId)}/advideos`,'GET',clean({fields:'id,status,picture',limit:100,after,title}));\n const video=Array.isArray(result.data)?result.data.find((v:Row)=>String(v.id)===a.videoId):undefined;\n if(video)return {videoId:a.videoId,state:video.status?.video_status??'unknown',ready:video.status?.video_status==='ready',thumbnailUrl:typeof video.picture==='string'?video.picture:undefined};\n if(page===0){\n try{const node=await graph(a.videoId,'GET',{fields:'id,title'});if(String(node.id)===a.videoId&&typeof node.title==='string'&&node.title.trim()){title=node.title;after=undefined;continue;}}catch{/* Keep the account-scoped lookup; metadata failure is not readiness. */}\n }\n const next=result.paging?.cursors?.after;if(!result.paging?.next||!next||next===after)break;after=next;\n }\n return {videoId:a.videoId,state:'not_found_in_recent_library',ready:false};\n });\n read('meta_get_adset_configuration','Read an existing ad set, its parent campaign budget/objective and account currency before preparing an edit. Returns native settings; no mutation.',{adSetId:id},async a=>{\n const adset=await owned(a.adSetId,a.adAccountId,'id,name,account_id,campaign_id,status,targeting,promoted_object,optimization_goal,billing_event,bid_strategy,bid_amount,bid_constraints,daily_budget,lifetime_budget,start_time,end_time,attribution_spec,destination_type,is_dynamic_creative,dsa_beneficiary,dsa_payor');\n const [info,campaign]=await Promise.all([account(a.adAccountId),owned(adset.campaign_id,a.adAccountId,'id,name,account_id,objective,status,daily_budget,lifetime_budget,special_ad_categories')]);\n return {account:info,campaign,adset,budgetLevel:Number(campaign.daily_budget)>0||Number(campaign.lifetime_budget)>0?'campaign':'adset',\n note:'Native money fields are minor units. Targeting updates replace the complete spec. Duplicate an existing ad set to preserve complex settings.'};\n });\n read('meta_get_catalog_batch_status','Read the completion and per-item errors for a catalog batch handle. Acceptance of a batch is not proof that every product succeeded.',{catalogId:id,handle:z.string().min(1).max(1000)},async a=>{await catalog(a.catalogId,a.adAccountId);return graph(`${a.catalogId}/check_batch_request_status`,'GET',{handle:a.handle,load_ids_of_invalid_requests:true});});\n return;\n }\n type Plan={path:string,payload:Row,scope?:()=>Promise<unknown>,readback?:string,validate?:boolean,redact?:Row,async?:boolean,idKey?:string,file?:{base64:string,name:string}};\n function register(n:string,description:string,shape:ToolShape,build:(a:Row)=>Plan|Promise<Plan>){\n const described=Object.fromEntries(Object.entries({adAccountId:accountId,...shape,...mode}).map(([key,value])=>[key,value.description?value:value.describe(parameterDescriptions[key]||key)]));\n const schema=z.object(described).strict();\n c.tool(n,description+' Local preview by default. No automatic retry. Account ownership is checked before Meta validation or mutation.',schema.shape,async raw=>{\n try{\n const a:Row=schema.parse(raw);if(a.confirm&&a.validateOnly)throw new Error('confirm and validateOnly cannot both be true.');\n const p=await build(a);safeJson(p.payload);\n if(a.validateOnly&&!p.validate)throw new Error('This endpoint does not expose validate_only. Use the local preview, then confirm.');\n if(!a.confirm&&!a.validateOnly)return out({applied:false,action:n,adAccountId:`act_${act(a.adAccountId)}`,payload:p.redact??p.payload,validation:{local:true,meta:false,ownershipChecked:false},nextAction:'Review these exact parameters. Use validateOnly where supported, or repeat with confirm:true to apply.'});\n await p.scope?.();\n const result=await graph(p.path,'POST',{...p.payload,...(a.validateOnly?{execution_options:['validate_only']}: {})},p.file);\n if(a.validateOnly)return out({applied:false,validatedByMeta:true,action:n,result});\n let verification:unknown;\n if(p.readback){try{verification=await graph(result[p.idKey||'id']||p.path,'GET',{fields:p.readback});}catch{verification={confirmed:false,message:'Mutation returned success, but readback failed. Read the returned ID; do not recreate it.'};}}\n if(['meta_create_campaign','meta_create_adset','meta_create_ad'].includes(n)&&obj(verification).confirmed!==false){\n verification={...obj(verification),confirmed:obj(verification).status==='PAUSED'&&String(obj(verification).account_id)===act(a.adAccountId)};\n }\n const differences:Row[]=[];\n if(n==='meta_create_adcreative'&&obj(verification).confirmed!==false){\n const media=mediaDifferences(a.spec.object_story_spec,obj(verification).object_story_spec,'object_story_spec');\n media.push(...mediaDifferences(a.spec.asset_feed_spec,obj(verification).asset_feed_spec,'asset_feed_spec'));\n differences.push(...media);\n verification={...obj(verification),confirmed:media.length===0&&String(obj(verification).account_id)===act(a.adAccountId),...(media.length?{message:'Meta returned different or missing media. For collections, check the linked Instant Experience cover. Inspect this existing creative before continuing; do not automatically recreate it.'}:{})};\n }\n if(n==='meta_create_adcreative'&&typeof obj(verification).name==='string'&&obj(verification).name!==a.name){\n differences.push({field:'name',requested:a.name,returned:obj(verification).name,\n note:'The submitted name and Meta readback differ. The cause is not established by this response; do not recreate the creative to force a match.'});\n }\n return out({applied:true,action:n,...(p.async?{processing:'accepted',message:'Accepted for asynchronous processing; this is not confirmation that every item or video is ready.'}:{}),result,verification,...(differences.length?{differences}:{})});\n }catch(error){const e=error as Error&{code?:number;subcode?:number;outcome?:string};return out({error:e.message,code:e.code,subcode:e.subcode,outcome:e.outcome||'not_applied',retrySafe:false,...(e.subcode===1341012?{nextAction:'Meta refused access to the Page/profile used by this creative. Verify the selected Page, its asset assignments and the token permissions. Repeating the same request will not repair this access.'}:{})},true);}\n });\n }\n const checkCurrency=async(a:Row)=>{const info=await account(a.adAccountId);if(a.currency&&info.currency!==a.currency)throw new Error(`Account currency is ${info.currency}, not ${a.currency}. Nothing was changed.`);};\n const budgets={currency:money.currency,dailyBudget:money.dailyBudget.describe('Daily budget in major account-currency units. Mutually exclusive with lifetimeBudget.'),lifetimeBudget:money.lifetimeBudget.describe('Total lifetime budget in major account-currency units. Requires bounded ad set schedules; mutually exclusive with dailyBudget.')};\n const budgetPayload=(a:Row,required=false):Row=>{\n if(has(a.dailyBudget)&&has(a.lifetimeBudget))throw new Error('Choose dailyBudget OR lifetimeBudget, not both.');\n if(required&&!has(a.dailyBudget)&&!has(a.lifetimeBudget))throw new Error('Provide dailyBudget or lifetimeBudget.');\n if((has(a.dailyBudget)||has(a.lifetimeBudget))&&!a.currency)throw new Error('Supply the account currency for monetary amounts.');\n const p:Row={};for(const [from,to]of [['dailyBudget','daily_budget'],['lifetimeBudget','lifetime_budget']])if(has(a[from])){p[to]=toMinorUnits(Number(a[from]),String(a.currency));if(p[to]<1)throw new Error('Budget rounds to zero in the account currency.');}return p;\n };\n register('meta_create_campaign','Create a PAUSED Meta campaign with either ad set budgets (omit campaign budget), a daily campaign budget, or a lifetime campaign budget. Supports native validate_only.',{\n name,objective:z.enum(['OUTCOME_TRAFFIC','OUTCOME_SALES','OUTCOME_LEADS','OUTCOME_AWARENESS','OUTCOME_ENGAGEMENT','OUTCOME_APP_PROMOTION']),\n specialAdCategories:z.array(z.enum(['NONE','HOUSING','EMPLOYMENT','CREDIT','ISSUES_ELECTIONS_POLITICS'])).optional().describe('Required special categories; omit for none.'),\n adsetBudgetSharing:z.boolean().default(false).describe('Explicit ad set budget sharing choice; only with ad set budgets.'),\n ...budgets,bidStrategy:adsetConfig.shape.bid_strategy,startTime:date.optional(),endTime:date.optional(),\n },a=>{\n const budget=budgetPayload(a);\n if(Object.keys(budget).length&&a.adsetBudgetSharing)throw new Error('Ad set budget sharing cannot be combined with a campaign budget.');\n if(a.startTime&&a.endTime&&Date.parse(a.endTime)<=Date.parse(a.startTime))throw new Error('endTime must follow startTime.');\n return {path:`act_${act(a.adAccountId)}/campaigns`,payload:clean({name:a.name,objective:a.objective,special_ad_categories:a.specialAdCategories??[],is_adset_budget_sharing_enabled:a.adsetBudgetSharing,status:'PAUSED',...budget,bid_strategy:a.bidStrategy,start_time:a.startTime,stop_time:a.endTime}),validate:true,scope:()=>checkCurrency(a),readback:'id,name,account_id,status,daily_budget,lifetime_budget'};\n });\n const adsetUpdate=(a:Row,payload:Row):Plan=>({path:a.adSetId,payload,validate:true,readback:'id,account_id,status,targeting,optimization_goal,billing_event,start_time,end_time,daily_budget,lifetime_budget,bid_strategy,bid_amount',scope:async()=>{\n await checkCurrency(a);const current=await owned(a.adSetId,a.adAccountId,'id,account_id,campaign_id,start_time,end_time,targeting,optimization_goal,billing_event,bid_strategy,bid_amount,bid_constraints,daily_budget,lifetime_budget');\n validateAdsetState({...current,...payload});\n if(payload.daily_budget||payload.lifetime_budget){const parent=await owned(current.campaign_id,a.adAccountId,'id,account_id,daily_budget,lifetime_budget');if(Number(parent.daily_budget)>0||Number(parent.lifetime_budget)>0)throw new Error('This campaign owns its budget; change it at campaign level.');}\n }});\n register('meta_update_adset_budget','Change an ad set daily or lifetime budget after checking its parent budget and existing schedule. Status stays unchanged.',{adSetId:id,...budgets},a=>adsetUpdate(a,budgetPayload(a,true)));\n register('meta_update_adset_schedule','Change start and/or end time using ISO 8601 with an explicit offset. Check the resulting range against existing dates before any mutation.',{adSetId:id,startTime:date.optional(),endTime:date.optional()},a=>{\n if(!a.startTime&&!a.endTime)throw new Error('Provide startTime or endTime.');\n const p=clean({start_time:a.startTime,end_time:a.endTime});if(a.startTime&&a.endTime&&Date.parse(a.endTime)<=Date.parse(a.startTime))throw new Error('endTime must follow startTime.');return adsetUpdate(a,p);\n });\n register('meta_update_campaign_budget','Change an existing campaign-owned budget after verifying currency and ownership. Does not convert an ad-set-budget campaign or change status.',{campaignId:id,...budgets},a=>({path:a.campaignId,payload:budgetPayload(a,true),validate:true,readback:'id,account_id,status,daily_budget,lifetime_budget',scope:async()=>{\n await checkCurrency(a);const parent=await owned(a.campaignId,a.adAccountId,'id,account_id,daily_budget,lifetime_budget');if(!(Number(parent.daily_budget)>0||Number(parent.lifetime_budget)>0))throw new Error('This campaign has no campaign budget; update its ad set budget.');\n }}));\n register('meta_create_adset','Create a PAUSED Meta ad set with explicit targeting, optimization, placements, attribution, bidding and schedule. Amounts use major account-currency units.',{campaignId:id,name,configuration:adsetConfig,...money},a=>{\n const payload=buildAdsetPayload(a,true);\n return {path:`act_${act(a.adAccountId)}/adsets`,payload,validate:true,readback:'id,name,status,effective_status,account_id,campaign_id,targeting,daily_budget,lifetime_budget',scope:async()=>{\n await checkCurrency(a);const campaign=await owned(a.campaignId,a.adAccountId,'id,account_id,objective,daily_budget,lifetime_budget,special_ad_categories');\n const cbo=Number(campaign.daily_budget)>0||Number(campaign.lifetime_budget)>0;\n if(cbo&&(payload.daily_budget||payload.lifetime_budget))throw new Error('This campaign owns its budget. Omit the ad set budget.');\n if(!cbo&&!payload.daily_budget&&!payload.lifetime_budget)throw new Error('This campaign has no campaign budget. Supply an ad set budget.');\n }};\n });\n register('meta_update_adset_configuration','Update targeting, optimization, placements, attribution, bidding, budget or schedule. Targeting is a COMPLETE replacement, not a partial merge. Status is unchanged.',{adSetId:id,configuration:adsetConfig,...money},a=>{\n const payload=buildAdsetPayload(a,false);\n return adsetUpdate(a,payload);\n });\n register('meta_duplicate_adset','Duplicate a known-good ad set within the selected account, preserving complex native configuration. Always PAUSED and WITHOUT copying ads. Rename it afterwards with meta_rename_adset.',{adSetId:id,campaignId:id.optional(),startTime:date.optional(),endTime:date.optional()},a=>{\n if(a.startTime&&a.endTime&&Date.parse(a.endTime)<=Date.parse(a.startTime))throw new Error('endTime must be after startTime.');\n return {path:`${a.adSetId}/copies`,payload:clean({campaign_id:a.campaignId,start_time:a.startTime,end_time:a.endTime,status_option:'PAUSED',deep_copy:false}),scope:async()=>{await owned(a.adSetId,a.adAccountId);if(a.campaignId)await owned(a.campaignId,a.adAccountId);},idKey:'copied_adset_id',readback:'id,name,status,account_id,campaign_id'};\n });\n register('meta_create_adcreative','Create an ad creative from an existing post or an explicit object_story_spec and optional asset_feed_spec. Supports image, video, carousel, collection and flexible specs. Does not launch an ad.',{name,spec:creativeSpec},a=>{\n const spec=creativeSpec.parse(a.spec);\n if(Number(!!spec.object_story_id)+Number(!!spec.object_story_spec)!==1)throw new Error('Supply exactly one of object_story_id or object_story_spec.');\n if(spec.object_story_spec&&!id.safeParse(spec.object_story_spec.page_id).success)throw new Error('object_story_spec requires an explicit numeric page_id.');\n return {path:`act_${act(a.adAccountId)}/adcreatives`,payload:{name:a.name,...spec},validate:true,scope:()=>account(a.adAccountId),readback:'id,name,account_id,object_story_id,effective_object_story_id,object_story_spec,asset_feed_spec,product_set_id'};\n });\n register('meta_create_ad','Create a PAUSED ad using an existing ad set and creative from the selected account.',{adSetId:id,creativeId:id,name,trackingSpecs:z.array(json).optional()},a=>({path:`act_${act(a.adAccountId)}/ads`,payload:clean({name:a.name,adset_id:a.adSetId,creative:{creative_id:a.creativeId},status:'PAUSED',tracking_specs:a.trackingSpecs}),validate:true,scope:async()=>{await owned(a.adSetId,a.adAccountId);await owned(a.creativeId,a.adAccountId);},readback:'id,name,account_id,status,effective_status,adset_id,creative{id}'}));\n register('meta_update_ad_creative','Replace an ad’s creative with another existing creative from the same account. The ad status is unchanged.',{adId:id,creativeId:id},a=>({path:a.adId,payload:{creative:{creative_id:a.creativeId}},validate:true,scope:async()=>{await owned(a.adId,a.adAccountId);await owned(a.creativeId,a.adAccountId);},readback:'id,account_id,status,creative{id}'}));\n register('meta_upload_ad_image','Upload an image into the selected ad account using base64 bytes (up to 5 MiB decoded). Preview reports size only, never the file body.',{bytesBase64:z.string().min(4).max(7_000_000).regex(/^[A-Za-z0-9+/]+={0,2}$/)},a=>{\n let bytes:string;try{bytes=atob(a.bytesBase64);}catch{throw new Error('Invalid base64 image.');}if(bytes.length>5*1024*1024)throw new Error('Image exceeds 5 MiB.');\n return {path:`act_${act(a.adAccountId)}/adimages`,payload:{bytes:a.bytesBase64},redact:{decodedBytes:bytes.length},scope:()=>account(a.adAccountId)};\n });\n register('meta_upload_ad_video','Import a video from a publicly reachable HTTPS URL or base64 MP4 bytes (up to 5 MiB decoded). Meta processes it asynchronously. No campaign is launched.',{fileUrl:z.string().url().startsWith('https://').optional(),bytesBase64:z.string().min(4).max(7_000_000).regex(/^[A-Za-z0-9+/]+={0,2}$/).optional(),title:name.optional()},a=>{\n if(Number(!!a.fileUrl)+Number(!!a.bytesBase64)!==1)throw new Error('Supply exactly one of fileUrl or bytesBase64.');\n let decodedBytes:number|undefined;if(a.bytesBase64){try{decodedBytes=atob(a.bytesBase64).length;}catch{throw new Error('Invalid base64 video.');}if(decodedBytes>5*1024*1024)throw new Error('Video exceeds 5 MiB. Use a public media URL for larger files.');}\n return {path:`act_${act(a.adAccountId)}/advideos`,payload:clean({file_url:a.fileUrl,title:a.title}),redact:clean({fileUrl:a.fileUrl,title:a.title,decodedBytes}),...(a.bytesBase64?{file:{base64:a.bytesBase64,name:'video.mp4'}}:{}),scope:()=>account(a.adAccountId),async:true};\n });\n register('meta_create_custom_audience','Create a website, engagement, customer-list container or lookalike audience. Does not upload customer data. Terms and eligibility are enforced by Meta.',{name,subtype:z.enum(['CUSTOM','WEBSITE','ENGAGEMENT','LOOKALIKE']),description:z.string().max(2000).optional(),rule:json.optional(),retentionDays:z.number().int().min(1).max(180).optional(),pixelId:id.optional(),originAudienceId:id.optional(),lookalikeSpec:json.optional(),customerFileSource:z.enum(['USER_PROVIDED_ONLY','PARTNER_PROVIDED_ONLY','BOTH_USER_AND_PARTNER_PROVIDED']).optional()},a=>{\n if(a.subtype==='LOOKALIKE'&&(!a.originAudienceId||!a.lookalikeSpec))throw new Error('A lookalike requires originAudienceId and lookalikeSpec.');\n if(a.subtype==='CUSTOM'&&!a.customerFileSource)throw new Error('Declare customerFileSource explicitly.');\n if(['WEBSITE','ENGAGEMENT'].includes(a.subtype)&&!a.rule)throw new Error('This audience requires a native rule.');\n return {path:`act_${act(a.adAccountId)}/customaudiences`,payload:clean({name:a.name,subtype:a.subtype,description:a.description,rule:a.rule,retention_days:a.retentionDays,pixel_id:a.pixelId,origin_audience_id:a.originAudienceId,lookalike_spec:a.lookalikeSpec,customer_file_source:a.customerFileSource}),scope:async()=>{await account(a.adAccountId);if(a.originAudienceId)await owned(a.originAudienceId,a.adAccountId);},readback:'id,name,account_id,subtype'};\n });\n register('meta_update_custom_audience','Update an existing custom audience name, description, retention or complete rule. Does not change audience membership directly.',{audienceId:id,name:name.optional(),description:z.string().max(2000).optional(),retentionDays:z.number().int().min(1).max(180).optional(),rule:json.optional()},a=>{\n const payload=clean({name:a.name,description:a.description,retention_days:a.retentionDays,rule:a.rule});if(!Object.keys(payload).length)throw new Error('No audience change supplied.');\n return {path:a.audienceId,payload,scope:()=>owned(a.audienceId,a.adAccountId),readback:'id,name,account_id,subtype'};\n });\n register('meta_create_product_set','Create a filtered product set in a catalog owned by the same Business as the selected ad account. Never publishes to Shops.',{catalogId:id,name,filter:json},a=>({path:`${a.catalogId}/product_sets`,payload:{name:a.name,filter:a.filter},scope:()=>catalog(a.catalogId,a.adAccountId),readback:'id,name,filter'}));\n register('meta_update_product_set','Replace a product set filter or rename it. Catalog ownership and set membership are checked before writing.',{catalogId:id,productSetId:id,name:name.optional(),filter:json.optional()},a=>{\n const payload=clean({name:a.name,filter:a.filter});if(!Object.keys(payload).length)throw new Error('No product set change supplied.');\n return {path:a.productSetId,payload,scope:async()=>{await catalog(a.catalogId,a.adAccountId);const set=await graph(a.productSetId,'GET',{fields:'id,product_catalog{id}'});if(String(set.product_catalog?.id)!==a.catalogId)throw new Error('Product set does not belong to the declared catalog.');},readback:'id,name,filter'};\n });\n register('meta_batch_catalog_items','Create or update up to 50 catalog products by retailer_id. Returns asynchronous batch handles; acceptance is not completion. No deletion or automatic upsert.',{catalogId:id,requests:z.array(z.object({method:z.enum(['CREATE','UPDATE']),retailer_id:z.string().min(1).max(100),data:json}).strict()).min(1).max(50)},a=>({path:`${a.catalogId}/batch`,payload:{requests:a.requests,allow_upsert:false},scope:()=>catalog(a.catalogId,a.adAccountId),async:true}));\n}\n\n/** Legacy mutations also need an account/object ownership check: an account\n * parameter alone must not authorize a node owned by a different client. */\nexport async function verifyMetaWriteScope(config:Record<string,string>,a:Row):Promise<void>{\n accountId.parse(a.adAccountId);\n const get=async(node:string,fields:string)=>{\n const response=await fetch(`https://graph.facebook.com/${config.apiVersion||'v26.0'}/${node}?fields=${encodeURIComponent(fields)}`,{headers:{authorization:`Bearer ${config.accessToken}`},signal:AbortSignal.timeout(30000),redirect:\"error\"});\n const data=await response.json() as Row;if(!response.ok||data.error)throw new Error('Cannot verify the Meta target scope. Nothing was changed.');return data;\n };\n const info=await get(`act_${act(a.adAccountId)}`,'id,currency');\n if(a.currency&&info.currency!==a.currency)throw new Error(`Account currency is ${info.currency}, not ${a.currency}. Nothing was changed.`);\n const target=a.adId||a.adSetId||a.campaignId;\n if(target){id.parse(target);const entity=await get(target,'id,account_id');if(String(entity.account_id)!==act(a.adAccountId))throw new Error('The target belongs to a different ad account. Nothing was changed.');}\n}\n","/** Copyright 2026 GetMCPAds. SPDX-License-Identifier: Apache-2.0 */\n/**\n * Conversions de montants vers l'unité attendue par chaque plateforme.\n *\n * Un assistant qui compose un appel raisonne en euros. Les quatre plateformes\n * attendent quatre unités différentes, et se tromper d'unité ne produit pas\n * une erreur : Google accepte volontiers un budget de 12 micros, soit 0,000012\n * unité, ou de 12 000 000 000 micros si le facteur est appliqué deux fois.\n * L'appel réussit, et c'est le compte du client qui encaisse.\n *\n * D'où des fonctions nommées, testées, plutôt que des multiplications posées\n * en ligne dans chaque outil.\n */\n\n/** Un montant que la plateforme peut recevoir : positif, fini, raisonnable. */\nfunction guard(amount: number, currency: string): number {\n if (!Number.isFinite(amount)) throw new Error(`Amount must be a finite number, received ${amount}.`);\n if (amount <= 0) throw new Error(`Amount must be greater than zero, received ${amount}.`);\n // Un million d'unités de devise par jour n'est pas un budget, c'est une\n // faute de frappe ou une double conversion.\n if (amount > 1_000_000) {\n throw new Error(\n `Amount ${amount} ${currency} is above the safety ceiling of 1,000,000. ` +\n \"If this is intentional, make the change in the platform's own interface.\",\n );\n }\n return amount;\n}\n\n/** Google Ads : micro-unités de la devise du compte. */\nexport function toMicros(amount: number): number {\n return Math.round(guard(amount, \"units\") * 1_000_000);\n}\n\n/**\n * Pinterest : micro-unités de la devise du compte, comme Google Ads.\n *\n * Les champs s'appellent `*_in_micro_currency` et `daily_spend_cap` suit la\n * même échelle. La passerelle a longtemps converti en millièmes : un plafond\n * demandé à 50 EUR partait à 0,05 EUR, la campagne s'arrêtait aussitôt et\n * l'appel répondait pourtant « appliqué ».\n */\nexport function toMicroCurrency(amount: number): number {\n return Math.round(guard(amount, \"units\") * 1_000_000);\n}\n\n/**\n * Meta : sous-unité de la devise du compte, en entier.\n *\n * Le facteur dépend de la devise. Le yen et le won n'ont pas de sous-unité,\n * donc y envoyer 1250 pour 12,50 multiplierait la dépense par cent.\n */\nconst ZERO_DECIMAL = new Set([\"JPY\", \"KRW\", \"CLP\", \"ISK\", \"VND\", \"UGX\", \"PYG\", \"RWF\", \"XOF\", \"XAF\", \"XPF\", \"BIF\", \"DJF\", \"GNF\", \"KMF\", \"MGA\", \"VUV\"]);\n\nexport function toMinorUnits(amount: number, currency: string): number {\n const code = currency.trim().toUpperCase();\n if (!/^[A-Z]{3}$/.test(code)) throw new Error(`Expected a three letter currency code, received \"${currency}\".`);\n const factor = ZERO_DECIMAL.has(code) ? 1 : 100;\n return Math.round(guard(amount, code) * factor);\n}\n\n/** TikTok reçoit le montant tel quel, dans la devise du compte. */\nexport function toPlainAmount(amount: number): number {\n return Number(guard(amount, \"units\").toFixed(2));\n}\n","/** Copyright 2026 GetMCPAds. SPDX-License-Identifier: Apache-2.0 */\nimport type { MetaApiRequest } from \"./client.js\";\n\nexport function measurementParams(\n request: MetaApiRequest,\n): Record<string, string> {\n const mode = request.attributionMode;\n const windows = request.attributionWindows;\n if (mode === \"explicit\" && (!windows || windows.length === 0))\n throw Error(\"Explicit attribution requires at least one window.\");\n if ((mode === \"account\" || mode === \"adset\") && windows?.length)\n throw Error(\n \"Choose native attribution settings or explicit windows, not both.\",\n );\n return {\n ...(mode === \"account\" ? { use_account_attribution_setting: \"true\" } : {}),\n ...(mode === \"adset\" ? { use_unified_attribution_setting: \"true\" } : {}),\n ...(windows?.length\n ? { action_attribution_windows: JSON.stringify(windows) }\n : {}),\n ...(request.actionReportTime\n ? { action_report_time: request.actionReportTime }\n : {}),\n };\n}\n\nexport function reportingEvidence(\n results: Record<string, unknown>[][],\n requests: MetaApiRequest[],\n joinKeys: string[],\n rows: Record<string, unknown>[],\n) {\n const key = (r: Record<string, unknown>) =>\n JSON.stringify(joinKeys.map((k) => r[k] ?? null));\n const indices = results.map(\n (group) => new Map(group.map((r) => [key(r), r])),\n );\n const missingRows = rows.filter(\n (r) =>\n requests.some((q) => q.fields.includes(\"spend\")) &&\n !Object.hasOwn(r, \"spend\"),\n );\n return {\n source_queries: requests.map((q, i) => ({\n index: i,\n type: q.type,\n fields: q.fields,\n breakdowns: q.breakdowns ?? [],\n row_count: results[i].length,\n measurement_parameters: measurementParams(q),\n pagination_exhausted: true,\n })),\n row_provenance: rows.map((r) => ({\n key: Object.fromEntries(joinKeys.map((k) => [k, r[k] ?? null])),\n exact_source_queries: indices.flatMap((m, i) =>\n m.has(key(r)) ? [i] : [],\n ),\n missing_requested_fields: [\n ...new Set(requests.flatMap((q) => q.fields)),\n ].filter((f) => !Object.hasOwn(r, f)),\n })),\n rows_without_spend: missingRows.length,\n notes: [\n \"Rows are merged from separate native queries; a missing field or missing query match is not proof of zero or of delivery outside the reporting period.\",\n \"Native attribution configuration is not inferred from numerical coincidences in action values. Explicit parameters are listed for each query.\",\n \"An action object may contain a window column without a value field. Preserve this absence; do not fill value from another window or label separate windows as a deduplicated combined total.\",\n \"Do not sum different purchase action types, attribution-window columns, or metrics broadcast across breakdown rows.\",\n \"Use native account_totals when requested for overall figures. Do not mix a subset spend denominator with conversions from another population.\",\n \"ROAS alone does not establish profitability. Missing counts do not establish future conversion growth.\",\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 { reportingEvidence, measurementParams } from \"./reporting-context.js\";\nimport { z } from \"zod\";\nimport { getBusinessAssets } from \"./business-assets.js\";\nimport { instagramInsightMetrics } from \"./organic-insights.js\";\nimport { metaCreativeMedia, metaImageFileUrl } from \"./creative-media.js\";\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 native effective_status, not configured status. PAUSED excludes IN_PROCESS, CAMPAIGN_PAUSED and ADSET_PAUSED. Omit for post-creation verification and inspect both returned status fields.\");\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 pageClientResolver(client: MetaClient, warnings: MetaWarning[]) {\n const pageClients = new Map<string, MetaClient | null>();\n const pageClientFor = async (id: string): Promise<MetaClient | null> => {\n if (!pageClients.has(id)) {\n const result = await fetchGraph(client, \"organic_page_token\", graphUrl(client, `/${encodeURIComponent(id)}`, {fields: \"access_token\"}), warnings,\n \"Grant pages_show_list/pages_read_engagement and access to this Page, then reconnect Meta if necessary.\");\n const token = typeof result?.access_token === \"string\" ? result.access_token : undefined;\n pageClients.set(id, token ? new MetaClient(token, client.apiVersion) : null);\n if (result && !token) warnings.push({area: \"organic_page_token\", message: \"Meta did not return a Page access token.\", suggestion: \"Verify this user's Page tasks and reconnect Meta. No user token is substituted for a Page token.\"});\n }\n return pageClients.get(id) ?? null;\n };\n return pageClientFor;\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 entityListResult(result: Record<string, unknown>, statusFilter?: string) {\n return {\n ...result,\n coverage: {\n scope: \"returned_page\",\n returned_count: dataArray(result).length,\n has_more: !!(isRecord(result.paging) && result.paging.next),\n status_filter_field: statusFilter ? \"effective_status\" : null,\n status_filter: statusFilter ?? null,\n },\n notes: [\n \"Follow paging.cursors.after while paging.next is present. A limited or filtered listing does not prove that an object does not exist.\",\n \"status is configured status; effective_status can differ during processing or due to a paused parent. Omit statusFilter when verifying a newly created object.\",\n \"An empty listing alone does not establish indexing lag or its cause. Read known campaign IDs with meta_get_campaign_structure.\",\n ],\n };\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 thumbnailSummary(value: Record<string, unknown> | undefined): Record<string, unknown> | undefined {\n if (!value || typeof value.uri !== \"string\") return undefined;\n return { uri: value.uri, width: value.width, height: value.height };\n}\n\nfunction normalizeAdVideo(video: Record<string, unknown>, includeSource: boolean): Record<string, unknown> {\n const status = isRecord(video.status) ? video.status : undefined;\n const thumbnailEdge = isRecord(video.thumbnails) ? video.thumbnails.data : undefined;\n const thumbnails = Array.isArray(thumbnailEdge) ? thumbnailEdge.filter(isRecord) : [];\n const preferred = thumbnails.find((thumb) => thumb.is_preferred === true) ?? thumbnails[0];\n let largest: Record<string, unknown> | undefined;\n for (const thumb of thumbnails) {\n if (!largest || (Number(thumb.width) || 0) > (Number(largest.width) || 0)) largest = thumb;\n }\n\n const from = isRecord(video.from) ? video.from : undefined;\n\n return {\n id: video.id,\n title: video.title,\n video_status: typeof status?.video_status === \"string\" ? status.video_status : undefined,\n duration_seconds: typeof video.length === \"number\" ? video.length : undefined,\n owner: from ? { id: from.id, name: from.name } : undefined,\n created_time: video.created_time,\n updated_time: video.updated_time,\n picture: video.picture,\n permalink_url: video.permalink_url,\n thumbnail_preferred: thumbnailSummary(preferred),\n thumbnail_largest: thumbnailSummary(largest),\n thumbnail_count: thumbnails.length,\n ...(includeSource ? { source: video.source } : {}),\n };\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 ...(() => { const media = metaCreativeMedia(creative); return { creative_format: media.format, media: media.media }; })(),\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_product_type: media.media_product_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. With adAccountId, discover only its owning Business; otherwise list /me/businesses when this is omitted.\"),\n adAccountId: adAccountIdSchema.optional().describe(\"Optional ad account ID to discover its owner Business and assigned pixels; datasets are read from that Business when permitted.\"),\n limit: limitSchema,\n cursor: cursorSchema.describe(\"Legacy cursor for /me/businesses only. Use cursors for asset edges.\"),\n cursors: z.record(z.string(), z.string()).optional().describe(\"Per-edge cursors from paging/nextActions. Never reuse a cursor for another edge.\"),\n maxPages: z.number().int().min(1).max(5).default(3).describe(\"Maximum pages per edge; remaining pages are reported explicitly.\"),\n },\n async (args) => { try { return ok(await getBusinessAssets(client, args)); } catch(e) { return formatMcpToolError(e); } },\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 ad-linked creative media with actual ad names, collection covers, all carousel/flexible components, resolved image hashes and video URLs. Catalog-only ads are classified separately; use the hosted GetMCPAds service for an interactive visual gallery. Raw creative metadata remains available for diagnostics.\",\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,image_hash,video_id,product_set_id,link_url,call_to_action_type,effective_object_story_id,object_type,object_story_spec,asset_feed_spec\";\n const adFields = `id,name,account_id,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 (const id of creativeIds.slice(0,20)) {\n const creative = await fetchGraph(client,\"creative_node\",graphUrl(client,`/${encodeURIComponent(id)}`,{fields:creativeFields}),warnings,\"Verify creative access, or use adIds with adAccountId for ad names.\");\n if(creative) merged[id]=creative;\n }\n result=merged;\n if(creativeIds.length>20) warnings.push({area:\"creative_assets\",message:\"Only 20 creative IDs resolved.\",suggestion:\"Use batches of 20 creative IDs, or adIds with adAccountId.\"});\n } else if (adIds && adIds.length > 0 && !adAccountId) {\n const merged: Record<string, unknown> = {};\n for (const id of adIds.slice(0, 20)) {\n const ad = await fetchGraph(client,\"ad_creative\",graphUrl(client,`/${encodeURIComponent(id)}`,{fields:adFields}),warnings,\"Provide the owning adAccountId for efficient targeted ad lookup.\");\n if (ad) merged[id] = ad;\n }\n if(adIds.length>20) warnings.push({area:\"creative_assets\",message:\"Only 20 ad IDs resolved without an account.\",suggestion:\"Provide adAccountId to resolve the full selection.\"});\n result=merged;\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, `/${formatAdAccountId(adAccountId!)}/ads`, { filtering: JSON.stringify([{field:\"id\",operator:\"IN\",value:chunk}]), fields: adFields, limit: 500 }),\n warnings,\n \"Verify the ad IDs are readable by this token and belong to accessible ad accounts.\",\n );\n for (const ad of dataArray(chunkResult)) if (ad.id) merged[String(ad.id)] = ad;\n }\n const missingIds = adIds.filter(id => !merged[id]);\n // Account inventory can omit archived/deleted ads still present in\n // historical insights. Read those exact nodes rather than invent media.\n for (const id of missingIds.slice(0, 10)) {\n const ad = await fetchGraph(client,\"historical_ad_creative\",graphUrl(client,`/${encodeURIComponent(id)}`,{fields:adFields}),warnings,\"Historical ad metadata may no longer be accessible.\");\n if (ad && typeof ad.account_id === \"string\" && formatAdAccountId(ad.account_id) === formatAdAccountId(adAccountId!)) merged[id] = ad;\n else if (ad) warnings.push({area:\"creative_assets\",message:`Ad ${id} was excluded because its owning account could not be verified against the requested account.`,suggestion:\"Use an ad from the selected account.\"});\n }\n if (missingIds.length > 10) warnings.push({area:\"creative_assets\",message:\"Historical ad fallback limited to 10 missing ads.\",suggestion:\"Narrow the ad selection to resolve additional historical ads.\"});\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 // Resolve hashes and videos without replacing individual carousel media\n // with the parent creative's (possibly product) thumbnail.\n const allMedia = assets.flatMap(asset => Array.isArray(asset.media) ? asset.media as import(\"./creative-media.js\").MetaMedia[] : []);\n // Always refresh hashes, even when Meta also returns a stale/small preview.\n const hashes = [...new Set(allMedia.filter(m => m.image_hash).map(m => m.image_hash!))];\n if (hashes.length && adAccountId) {\n for (let offset = 0; offset < hashes.length; offset += 50) {\n const images = await fetchGraph(client, \"creative_image_hashes\", graphUrl(client, `/${formatAdAccountId(adAccountId)}/adimages`, {\n hashes: JSON.stringify(hashes.slice(offset, offset + 50)), fields: \"hash,permalink_url,url\", limit: 50,\n }), warnings, \"Verify ads_read access to the ad account image library.\");\n for (const image of dataArray(images)) for (const item of allMedia) if (item.image_hash === image.hash) item.image_url = metaImageFileUrl(image.url, image.permalink_url, item.image_url);\n }\n }\n const videoIds = [...new Set(allMedia.map(m => m.video_id).filter((id): id is string => Boolean(id)))];\n const pageClientFor = pageClientResolver(client, warnings);\n for (const id of videoIds.slice(0, 10)) {\n const video = await fetchGraph(client, \"creative_video\", graphUrl(client, `/${encodeURIComponent(id)}`, {fields:\"id,picture,source,from\"}), warnings,\n \"Verify access to the ad video and its owning Page.\") ?? {id};\n const owner = assets.find(asset => (asset.media as import(\"./creative-media.js\").MetaMedia[]).some(m => m.video_id === id));\n const pageId = firstString(isRecord(video.from) ? video.from.id : undefined, owner?.page_id,\n typeof owner?.effective_object_story_id === \"string\" ? owner.effective_object_story_id.split(\"_\")[0] : undefined);\n if (!video.source && pageId) {\n const pageClient=await pageClientFor(pageId);\n if (pageClient) {\n const resolved=await fetchGraph(pageClient,\"creative_page_video\",graphUrl(pageClient,`/${encodeURIComponent(id)}`,{fields:\"id,picture,source\"}),warnings,\"Verify Page content access.\");\n if(resolved) Object.assign(video,resolved);\n }\n }\n for (const item of allMedia) if (item.video_id === id) {\n item.thumbnail_url = firstString(video?.picture, item.thumbnail_url);\n item.source = firstString(video?.source, item.source);\n }\n if (!video.source) warnings.push({area:\"creative_video\",message:`No playable source returned for video ${id}. A cover is not evidence of the full video.`,suggestion:\"Check the connected profile's access to the owning Page; re-resolve this video before playback or analysis.\"});\n }\n if (videoIds.length > 10) warnings.push({area:\"creative_video\",message:\"Video URL resolution limited to 10 videos.\",suggestion:\"Narrow the ad selection or use meta_get_video_sources to resolve additional videos.\"});\n\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 \"Read Facebook Page posts and Instagram media with URLs, native periods and optional insights. These are content insights, not Ads Insights; do not infer an organic-only breakdown.\",\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 // Fetch metadata independently: an unavailable insight must not hide the post.\n // Likes/comments edges require additional user-content access even with\n // limit(0). Keep the native shares count; do not request those edges.\n const pagePostFields = \"id,message,created_time,permalink_url,full_picture,attachments{media,type,url,target,title,description},shares\";\n const pagePostFallbackFields = \"id,message,created_time,permalink_url,full_picture,shares\";\n const igMediaFields = \"id,caption,media_type,media_product_type,media_url,thumbnail_url,permalink,timestamp,username,like_count,comments_count,children{id,media_type,media_url,thumbnail_url,permalink}\";\n const pageClientFor = pageClientResolver(client, warnings);\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 for (const postId of [...new Set(postIds)]) {\n const ownerId = /^(\\d+)_\\d+$/.exec(postId)?.[1] ?? pageId;\n if (!ownerId) {\n warnings.push({area: \"page_post_ids\", message: `Cannot resolve the owning Page for post ${postId}.`, suggestion: \"Provide pageId or the full PageID_PostID returned by Meta.\"});\n continue;\n }\n const pageClient = await pageClientFor(ownerId);\n if (!pageClient) continue;\n const post = await fetchGraphWithFallback(pageClient, \"page_post_ids\",\n graphUrl(pageClient, `/${encodeURIComponent(postId)}`, {fields: pagePostFields}),\n graphUrl(pageClient, `/${encodeURIComponent(postId)}`, {fields: pagePostFallbackFields}), warnings,\n \"Post attachments require Page content access.\");\n if (post) pagePosts.push(normalizeOrganicPost(post));\n }\n } else if (includePagePosts && pageId) {\n const pageClient = await pageClientFor(pageId);\n if (pageClient) {\n const result = await fetchGraphWithFallback(pageClient, \"page_posts_enrichment\",\n graphUrl(pageClient, `/${encodeURIComponent(pageId)}/posts`, {fields: pagePostFields, limit, after: cursor, since, until}),\n graphUrl(pageClient, `/${encodeURIComponent(pageId)}/posts`, {fields: pagePostFallbackFields, limit, after: cursor, since, until}), warnings,\n \"Post attachments require pages_read_engagement and Page access.\");\n pagePosts.push(...dataArray(result).map(normalizeOrganicPost));\n paging.page_posts = pagingInfo(result);\n }\n }\n\n if (includeInsights) {\n for (const post of pagePosts) {\n const id = typeof post.id === \"string\" ? post.id : undefined;\n const ownerId = id ? /^(\\d+)_\\d+$/.exec(id)?.[1] ?? pageId : undefined;\n if (!id || !ownerId) continue;\n const pageClient = await pageClientFor(ownerId);\n if (!pageClient) continue;\n post.insights = await fetchGraph(pageClient, `page_post:${id}:insights`,\n graphUrl(pageClient, `/${encodeURIComponent(id)}/insights`, {metric: \"post_media_view,post_clicks\"}), warnings,\n \"Page post insights require read_insights and a Page token. Unavailable metrics are not zero; post metadata is retained.\");\n }\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 metrics = instagramInsightMetrics(media);\n media.insights_requested_metrics = metrics;\n try {\n media.insights = await client.fetchUrl(graphUrl(client, `/${encodeURIComponent(mediaId)}/insights`, {metric: metrics.join(\",\")}));\n } catch (error) {\n warnings.push(warningFromError(`instagram_media:${mediaId}:insights`, error,\n \"Insight availability depends on media format, age and permissions. Unavailable metrics are not zero; media metadata is retained.\"));\n // One bounded fallback for metric incompatibility only. Do not retry\n // authentication, permission, rate-limit or unavailable-media failures.\n const incompatible = error instanceof MetaApiException && error.code === 100\n && /metric.*(incompat|valid|supported|must be)|invalid.*metric/i.test(error.message);\n media.insights = incompatible && metrics.length > 1\n ? await fetchGraph(client, `instagram_media:${mediaId}:reach`,\n graphUrl(client, `/${encodeURIComponent(mediaId)}/insights`, {metric: \"reach\"}), warnings,\n \"Only reach was retried after Meta rejected the format-specific metric selection; other metrics remain unavailable.\")\n : null;\n }\n media.insights_returned_metrics = dataArray(isRecord(media.insights) ? media.insights : null).map(row => row.name);\n\n }\n }\n\n const insightState=(item:Record<string,unknown>,requested:string[])=>{\n const result=isRecord(item.insights)?item.insights:null;\n const returned=dataArray(result);\n return {requested:includeInsights,source:\"content_insights\",delivery_breakdown:\"not_established\",metrics:Object.fromEntries(requested.map(name=>{\n const metric=returned.find(m=>m.name===name);\n return [name,{status:!includeInsights?\"not_requested\":!result?\"read_failed\":!metric?\"not_returned\":\"returned\",period:metric?.period??null}];\n }))};\n };\n for(const post of pagePosts)post.insight_coverage=insightState(post,[\"post_media_view\",\"post_clicks\"]);\n for(const media of instagramMedia)media.insight_coverage=insightState(media,Array.isArray(media.insights_requested_metrics)?media.insights_requested_metrics:instagramInsightMetrics(media));\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 \"Facebook likes/comments edges are not requested. Missing engagement counts are unavailable, not zero; no commenter profiles are collected.\",\n \"Media insights retain Meta’s native periods (usually lifetime). since/until filter the media list, not the measurement period of each media insight.\",\n \"Instagram thumbnail_url is media-type dependent and may only appear for video/Reels media.\",\n \"This tool reads existing Page/Instagram content; it never publishes, edits, hides, or deletes posts.\",\n \"Content insights are not Ads Insights. Do not label a metric organic-only unless Meta supplies an explicit organic breakdown or definition.\",\n \"Preserve native metric names, titles, descriptions and periods. Unusual titles alone do not establish audience restrictions.\",\n \"Native Reel watch time is in milliseconds; do not add an unverified per-view denominator, infer duration from CDN URLs, or infer completion rates or causes of counter discrepancies.\",\n \"Keep full returned media URLs as clickable link targets; do not strip required signed query parameters. A media URL is different from a post permalink or an ad landing page.\",\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 = 100, 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(entityListResult(result, statusFilter));\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 = 100, 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(entityListResult(result, statusFilter));\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; adSetId is also accepted.\"),\n adSetId: z.string().optional().describe(\"Alias for adsetId. If both are supplied, they must match.\"),\n statusFilter: statusFilterSchema,\n limit: limitSchema,\n cursor: cursorSchema,\n },\n async ({ adAccountId, adsetId, adSetId, statusFilter, limit = 100, cursor }) => {\n try {\n if (adsetId && adSetId && adsetId !== adSetId) throw new Error(\"adsetId and adSetId must match when both are supplied.\");\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 ?? 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(entityListResult(result, statusFilter));\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 adIds: z.array(z.string().regex(/^\\d+$/)).min(1).max(100).optional().describe(\"Restrict ad-level insights to these ad IDs.\"),\n attributionMode: z.enum([\"account\", \"adset\", \"explicit\"]).default(\"account\").describe(\"Use Meta account settings, ad-set settings, or explicitly supplied windows. No setting is inferred from returned numbers.\"),\n attributionWindows: z.array(z.enum([\"1d_click\", \"7d_click\", \"28d_click\", \"1d_view\", \"1d_ev\"])).min(1).max(5).optional().describe(\"Only with attributionMode explicit. These windows are sent identically to every split query and account total.\"),\n actionReportTime: z.enum([\"impression\", \"conversion\", \"mixed\"]).default(\"impression\").describe(\"Date basis for actions, sent explicitly to Meta.\"),\n includeAccountTotals: z.boolean().default(false).describe(\"Also read native account-level totals with the same dates, metrics and attribution. Requires no adIds or breakdowns.\"),\n limit: z.number().int().min(1).max(5000).optional().default(500),\n },\n async ({ adAccountId, level, metrics, breakdowns, datePreset, timeRange, timeIncrement, adIds, limit, attributionMode = \"account\", attributionWindows, actionReportTime = \"impression\", includeAccountTotals = false }) => {\n try {\n const startTime = Date.now();\n\n // Plan the query\n if (adIds && level !== \"ad\") throw new Error(\"adIds requires level ad.\");\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 if (includeAccountTotals && (adIds?.length || breakdowns?.length)) throw new Error(\"Native account totals require no ad ID filter or breakdowns; run a separate account-level query instead.\");\n const measurement = {attributionMode, attributionWindows: attributionWindows ? [...new Set(attributionWindows)] : undefined, actionReportTime};\n const sentMeasurement = measurementParams({...plan.requests[0], ...measurement});\n for(const request of plan.requests) Object.assign(request, measurement);\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, ...(adIds ? {filtering:[{field:\"ad.id\",operator:\"IN\",value:adIds}]} : {})});\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 let accountTotals: Record<string, unknown>[] | null = null;\n let totalsRequestCount = 0;\n if(includeAccountTotals) {\n if(level === \"account\") accountTotals = data;\n else {\n const totalsPlan = planQueries({adAccountId,level:\"account\",metrics,breakdowns:[],datePreset:(datePreset??\"last_30d\") as import(\"./types.js\").DatePreset,timeRange,timeIncrement:timeIncrement as import(\"./types.js\").TimeIncrement|undefined,limit});\n if(totalsPlan.errors.length) throw new Error(\"Cannot produce comparable native account totals: \" + totalsPlan.errors.join(\"; \"));\n const results:Record<string,unknown>[][]=[];\n for(const q of totalsPlan.requests){results.push(await client.fetchInsights(adAccountId,{...q,...measurement}));totalsRequestCount++;}\n accountTotals=mergeResults(results,totalsPlan.joinKeys);\n if(totalsPlan.calculatedMetrics.length)accountTotals=accountTotals.map(row=>({...row,...calculateDerivedMetrics(row as import(\"./types.js\").InsightRow)}));\n }\n }\n const evidence=reportingEvidence(allResults,plan.requests,plan.joinKeys,data);\n return ok({\n data,\n reporting_context: {ad_account_id:adAccountId,level,time_range:timeRange??null,date_preset:timeRange?null:(datePreset??\"last_30d\"),time_increment:timeIncrement??\"all_days\",attribution:{mode:attributionMode,requested_windows:attributionWindows??null,resolved_native_windows:null,parameters_sent:sentMeasurement},action_report_time:actionReportTime,ad_ids:adIds??null,breakdowns:breakdowns??[],pagination_exhausted:true},\n account_totals: accountTotals,\n evidence,\n notes: evidence.notes,\n rowCount: data.length,\n debug: {\n requestCount: plan.requests.length + totalsRequestCount,\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 if (adIds && adIds.length > 0) {\n const ids = [...new Set(adIds)];\n if (ids.length > 500 || ids.some(id => !/^\\d+$/.test(id))) {\n throw new Error(\"Provide at most 500 numeric ad IDs.\");\n }\n const account = formatAdAccountId(adAccountId);\n const merged: Record<string, unknown> = {};\n const warnings: MetaWarning[] = [];\n let requestCount = 0;\n // Graph v26 removed the root ?ids= multi-node lookup. Query the\n // authorized account instead, retaining the legacy ID-keyed result.\n for (let index = 0; index < ids.length; index += 50) {\n const chunk = ids.slice(index, index + 50);\n const result = await client.fetchUrl(graphUrl(client, `/${account}/ads`, {\n fields: `account_id,${fields}`,\n filtering: JSON.stringify([{field: \"id\", operator: \"IN\", value: chunk}]),\n limit: 500,\n }));\n requestCount++;\n for (const ad of dataArray(result)) {\n if (typeof ad.id === \"string\" && chunk.includes(ad.id)\n && typeof ad.account_id === \"string\" && formatAdAccountId(ad.account_id) === account) {\n merged[ad.id] = ad;\n }\n }\n }\n // Historical ads may be missing from the account inventory. Bound\n // exact-node reads and verify ownership before returning their data.\n const missing = ids.filter(id => !merged[id]);\n for (const id of missing.slice(0, 10)) {\n requestCount++;\n const ad = await fetchGraph(client, \"historical_ad_creative\",\n graphUrl(client, `/${id}`, {fields: `account_id,${fields}`}), warnings,\n \"Verify that this historical ad is still accessible in the selected account.\");\n if (ad?.id === id && typeof ad.account_id === \"string\"\n && formatAdAccountId(ad.account_id) === account) merged[id] = ad;\n }\n const unresolved = ids.filter(id => !merged[id]);\n if (unresolved.length) warnings.push({area: \"creative_lookup\",\n message: `${unresolved.length} requested ads could not be verified in the selected account.`,\n suggestion: \"Narrow the selection; historical fallback is limited to 10 ads. Missing ads are not empty creatives.\"});\n return ok({...merged, warnings, debug: {requestCount}});\n }\n const result = await client.fetchUrl(graphUrl(client, `/${formatAdAccountId(adAccountId)}/ads`, {fields, limit}));\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 \"Read published Facebook Page posts using a Page token resolved from the connected user. Returns exact PageID_PostID, message, permalink and media attachments. Use postId for an exact existing-post ad preflight. No Instagram media, comments, likes or insights are requested; this read alone does not establish advertising eligibility.\",\n {\n pageId: z.string().regex(/^\\d+$/).describe(\"Facebook Page ID\"),\n postId: z.string().regex(/^\\d+_\\d+$/).optional().describe(\"Exact PageID_PostID to read instead of the recent-post list; must belong to pageId.\"),\n limit: z.number().int().min(1).max(100).optional().default(25),\n cursor: cursorSchema,\n },\n async ({ pageId, postId, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n if (postId && (!postId.startsWith(`${pageId}_`) || cursor)) {\n return { ...ok({error: \"postId must belong to pageId and cannot be combined with a list cursor.\"}), isError: true };\n }\n const pageClient = await pageClientResolver(client, warnings)(pageId);\n if (!pageClient) return { ...ok({error: \"Could not obtain access to the selected Page. No posts were read.\", warnings}), isError: true };\n const path = postId ? `/${encodeURIComponent(postId)}` : `/${encodeURIComponent(pageId)}/posts`;\n const params = postId ? {} : {limit, after: cursor};\n const result = await fetchGraphWithFallback(pageClient, \"page_posts\",\n graphUrl(pageClient, path, {...params, fields: \"id,message,created_time,permalink_url,full_picture,attachments{media,type,url,target,title,description},shares\"}),\n graphUrl(pageClient, path, {...params, fields: \"id,message,created_time,permalink_url,full_picture,shares\"}), warnings,\n \"Post attachments require pages_read_engagement and access to this Page.\");\n if (!result) return { ...ok({error: \"Could not read the selected Page posts.\", warnings}), isError: true };\n return ok({ ...(postId ? {data: [result]} : result), page_id: pageId,\n coverage: {scope: postId ? \"exact_post\" : \"page_posts\", has_more: !!(isRecord(result.paging) && result.paging.next)},\n warnings,\n notes: [\"Use the native PageID_PostID to reuse a post; the number in a Reel permalink is not its post ID.\", \"Advertising eligibility requires separate creative/ad validation by Meta. Missing engagement counts are unavailable, not zero.\"],\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 cursor: cursorSchema,\n },\n async ({ adAccountId, entityType, nameFilter, statusFilter, limit, cursor }) => {\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: Array<{field: string; operator: string; value: string | string[]}> = [{ field: \"name\", operator: \"CONTAIN\", value: nameFilter }];\n if (statusFilter) filters.push({ field: \"effective_status\", operator: \"IN\", value: [statusFilter] });\n const url = graphUrl(client, `/${formatAdAccountId(adAccountId)}/${entityType}`, {\n fields, limit, filtering: JSON.stringify(filters), after: cursor,\n });\n const result = await client.fetchUrl(url);\n return ok(entityListResult(result, statusFilter));\n } catch (e) {\n return formatMcpToolError(e);\n }\n },\n );\n\n registerMetaBroadReadTools(server, client, ok);\n // ── 16. meta_list_ad_images ───────────────────────────────────────\n server.tool(\n \"meta_list_ad_images\",\n \"List the ad account image library (/adimages): hash, name, dimensions, status, a permanent publicly served display URL (permalink_url), short-lived CDN URLs, and optionally the creatives using each image.\",\n {\n adAccountId: adAccountIdSchema,\n nameFilter: z.string().optional().describe(\"Only images whose file name matches this value\"),\n hashes: z.array(z.string()).optional().describe(\"Only these image hashes (exact match)\"),\n minWidth: z.number().int().min(1).optional().describe(\"Only images at least this wide, in pixels\"),\n minHeight: z.number().int().min(1).optional().describe(\"Only images at least this tall, in pixels\"),\n includeUsage: z.boolean().optional().default(false).describe(\"Also return the creative IDs using each image\"),\n limit: limitSchema,\n cursor: cursorSchema,\n },\n async ({ adAccountId, nameFilter, hashes, minWidth, minHeight, includeUsage, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const baseFields = [\n \"id\", \"hash\", \"name\", \"status\", \"created_time\", \"updated_time\",\n \"original_width\", \"original_height\", \"width\", \"height\",\n \"permalink_url\", \"url\", \"url_128\",\n ];\n if (includeUsage) baseFields.push(\"creatives\");\n\n const result = await fetchGraph(\n client,\n \"ad_images\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/adimages`, {\n fields: baseFields.join(\",\"),\n limit: typeof limit === \"number\" ? limit : 100,\n after: cursor,\n summary: \"total_count\",\n name: nameFilter,\n hashes: Array.isArray(hashes) && hashes.length > 0 ? JSON.stringify(hashes) : undefined,\n minwidth: minWidth,\n minheight: minHeight,\n }),\n warnings,\n \"Verify the token has ads_read on this ad account.\",\n );\n\n const images = dataArray(result).map((image) => ({\n ...image,\n creatives: Array.isArray(image.creatives) ? image.creatives : undefined,\n }));\n const summary = isRecord(result?.summary) ? result.summary : undefined;\n\n return ok({\n images,\n count: images.length,\n totalCount: typeof summary?.total_count === \"number\" ? summary.total_count : undefined,\n paging: pagingInfo(result),\n warnings,\n limitations: [\n \"Use url (or url_128 for a small preview) for image bytes. permalink_url can return Facebook HTML and is not an image file.\",\n \"CDN URLs are signed and can expire. Re-list by hash to refresh; do not store the URLs as durable links.\",\n ],\n nextActions: pagingInfo(result)\n ? [\"Pass the paging cursor as `cursor` to fetch the next page.\"]\n : [],\n });\n },\n );\n\n // ── 17. meta_list_ad_videos ───────────────────────────────────────\n server.tool(\n \"meta_list_ad_videos\",\n \"List the ad account video library (/advideos): title, duration, processing status, and publicly served thumbnails (preferred and largest sizes). Source file URLs are returned only on request because they expire quickly.\",\n {\n adAccountId: adAccountIdSchema,\n titleFilter: z.string().optional().describe(\"Only videos whose title contains this value\"),\n minLengthSeconds: z.number().min(0).optional().describe(\"Only videos at least this many seconds long\"),\n maxLengthSeconds: z.number().min(1).optional().describe(\"Only videos at most this many seconds long\"),\n includeSource: z.boolean().optional().default(false).describe(\"Also return the short-lived raw video file URL for each video\"),\n limit: z.number().int().min(1).max(200).optional().default(25).describe(\"Videos per page (thumbnail payloads are heavy, keep this modest)\"),\n cursor: cursorSchema,\n },\n async ({ adAccountId, titleFilter, minLengthSeconds, maxLengthSeconds, includeSource, limit, cursor }) => {\n const warnings: MetaWarning[] = [];\n const baseFields = [\n \"id\", \"title\", \"status\", \"created_time\", \"updated_time\", \"length\",\n \"picture\", \"permalink_url\",\n \"thumbnails{uri,width,height,scale,is_preferred}\",\n ];\n if (includeSource) baseFields.push(\"source\");\n\n const result = await fetchGraph(\n client,\n \"ad_videos\",\n graphUrl(client, `/${formatAdAccountId(adAccountId)}/advideos`, {\n fields: baseFields.join(\",\"),\n limit: typeof limit === \"number\" ? limit : 25,\n after: cursor,\n summary: \"total_count\",\n title: titleFilter,\n // Vérifié en live (Graph v26.0) : minlength/maxlength s'expriment en\n // millisecondes malgré une doc muette; en secondes, maxlength=120\n // élimine silencieusement toutes les vidéos.\n minlength: typeof minLengthSeconds === \"number\" ? Math.floor(minLengthSeconds * 1000) : undefined,\n maxlength: typeof maxLengthSeconds === \"number\" ? Math.ceil(maxLengthSeconds * 1000) : undefined,\n }),\n warnings,\n \"Verify the token has ads_read on this ad account.\",\n );\n\n const videos = dataArray(result).map((video) => normalizeAdVideo(video, Boolean(includeSource)));\n const summary = isRecord(result?.summary) ? result.summary : undefined;\n\n return ok({\n videos,\n count: videos.length,\n totalCount: typeof summary?.total_count === \"number\" ? summary.total_count : undefined,\n paging: pagingInfo(result),\n warnings,\n limitations: [\n \"Thumbnail and picture URLs are publicly served without authentication but carry CDN signatures that expire after a few weeks: re-list to refresh, do not store them as durable links.\",\n \"permalink_url points at facebook.com/watch and requires a logged-in session: unsuitable for hotlinking.\",\n includeSource\n ? \"source URLs are short-lived signed download links: use them immediately.\"\n : \"Call meta_get_video_sources with specific video IDs when you need downloadable source file URLs.\",\n ],\n nextActions: pagingInfo(result)\n ? [\"Pass the paging cursor as `cursor` to fetch the next page.\"]\n : [],\n });\n },\n );\n\n // ── 18. meta_get_video_sources ────────────────────────────────────\n server.tool(\n \"meta_get_video_sources\",\n \"Resolve fresh download URLs (source) and thumbnails for specific ad videos. Call this at download time: the returned URLs are signed and expire quickly.\",\n {\n adAccountId: adAccountIdSchema.describe(\"Ad account that owns the videos, used for scoping and rate limits\"),\n videoIds: z.array(z.string()).min(1).max(10).describe(\"Video IDs to resolve (at most 10 per call, one Graph request each)\"),\n pageId: z.string().regex(/^\\d+$/).optional().describe(\"Owning Page ID from the ad creative. Allows Page-token fallback even when the user token cannot read the video node.\"),\n },\n async ({ adAccountId, videoIds, pageId: owningPageId }) => {\n void adAccountId;\n const warnings: MetaWarning[] = [];\n // Le paramètre batch `?ids=` est déprécié depuis Graph v26.0 (code 100) :\n // chaque vidéo se lit sur son propre nœud, d'où le plafond de 10 par appel\n // (le Worker est limité à 50 sous-requêtes par invocation).\n const maxIdsPerCall = 10;\n const requested = Array.isArray(videoIds) ? [...new Set(videoIds.map(String).filter(Boolean))] : [];\n const ids = requested.slice(0, maxIdsPerCall);\n if (ids.length === 0) {\n return ok({\n videos: [],\n count: 0,\n warnings: [{\n area: \"video_sources\",\n message: \"No video IDs were provided.\",\n suggestion: \"Pass videoIds from meta_list_ad_videos.\",\n }],\n });\n }\n if (requested.length > maxIdsPerCall) {\n warnings.push({\n area: \"video_sources\",\n message: `Only the first ${maxIdsPerCall} of ${requested.length} video IDs were resolved.`,\n suggestion: \"Call again with the remaining IDs: each video costs one Graph request.\",\n });\n }\n\n const videoFields = \"id,title,status,length,source,picture,permalink_url,from,thumbnails{uri,width,height,is_preferred}\";\n const fetched: Record<string, unknown>[] = [];\n for (const videoId of ids) {\n const result = await fetchGraph(\n client,\n `video_${videoId}`,\n graphUrl(client, `/${encodeURIComponent(videoId)}`, { fields: videoFields }),\n warnings,\n \"Verify the video ID comes from meta_list_ad_videos and is readable by this token.\",\n );\n if (result) fetched.push(result);\n else if (owningPageId) fetched.push({id:videoId});\n }\n\n // Vérifié en live : une vidéo hébergée par une Page (post boosté…) ne\n // livre pas `source` au token du compte, mais le token de la Page :\n // dérivable du même token quand il administre la Page; le livre.\n const pageClients = new Map<string, MetaClient | null>();\n for (const video of fetched) {\n if (typeof video.source === \"string\") continue;\n const from = isRecord(video.from) ? video.from : undefined;\n const pageId = typeof from?.id === \"string\" ? from.id : owningPageId;\n if (!pageId) continue;\n\n let pageClient = pageClients.get(pageId);\n if (pageClient === undefined) {\n const tokenResult = await fetchGraph(\n client,\n `page_token_${pageId}`,\n graphUrl(client, `/${pageId}`, { fields: \"access_token\" }),\n warnings,\n \"Resolving a Page-hosted video file requires an admin role on the owning Page and the pages_show_list scope.\",\n );\n const pageAccessToken = typeof tokenResult?.access_token === \"string\" ? tokenResult.access_token : undefined;\n pageClient = pageAccessToken ? new MetaClient(pageAccessToken, client.apiVersion) : null;\n pageClients.set(pageId, pageClient);\n }\n if (!pageClient) continue;\n\n const retried = await fetchGraph(\n pageClient,\n `video_${video.id}_via_page`,\n graphUrl(pageClient, `/${encodeURIComponent(String(video.id))}`, { fields: videoFields }),\n warnings,\n \"The Page token could not read this video: verify the Page role covers content access.\",\n );\n if (retried && typeof retried.source === \"string\") Object.assign(video, retried);\n }\n\n const videos = fetched.map((video) => normalizeAdVideo(video, true));\n const withoutSource = videos.filter((video) => video.source === undefined).length;\n if (withoutSource > 0) {\n warnings.push({\n area: \"video_sources\",\n message: `${withoutSource} video(s) returned no source file even via their owning Page.`,\n suggestion: \"Their thumbnails and metadata remain available; the file requires a token holding an admin role on the owning Page.\",\n });\n }\n\n return ok({\n videos,\n count: videos.length,\n warnings,\n limitations: [\n \"source URLs are signed, publicly fetchable download links that expire quickly: use them immediately and re-call this tool for fresh ones.\",\n \"Videos owned by a Facebook Page (boosted posts) are resolved through the Page's own token when this connection administers the Page; otherwise thumbnails and metadata are returned without the file.\",\n ],\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 */\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 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 */\nimport { measurementParams } from \"./reporting-context.js\";\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\nconst META_REQUEST_TIMEOUT_MS = 15_000;\nconst MAX_PAGINATION_PAGES = 20;\nconst MAX_PAGINATION_ROWS = 10_000;\nconst MAX_PAGE_ROWS = 1_000;\nconst MAX_SEARCH_ENTITY_ROWS = 500;\n\nfunction trustedMetaGraphUrl(\n rawUrl: string,\n apiVersion: string,\n expectedPathname?: string,\n): URL {\n let parsed: URL;\n try {\n parsed = new URL(rawUrl);\n } catch {\n throw new Error(\"Meta Graph URL is invalid.\");\n }\n\n const versionSegment = parsed.pathname.split(\"/\").filter(Boolean)[0];\n if (\n parsed.protocol !== \"https:\"\n || parsed.origin !== META_GRAPH_API_BASE\n || parsed.username\n || parsed.password\n || parsed.port\n || parsed.hash\n || versionSegment !== apiVersion\n || (expectedPathname !== undefined && parsed.pathname !== expectedPathname)\n ) {\n throw new Error(\"Meta Graph URL failed the trusted origin, API-version, and route policy.\");\n }\n\n // Meta may repeat the same bearer in paging URLs. Keep authority in the\n // header so a URL is never the credential carrier.\n parsed.searchParams.delete(\"access_token\");\n return parsed;\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 attributionMode?: \"account\" | \"adset\" | \"explicit\";\n actionReportTime?: \"impression\" | \"conversion\" | \"mixed\";\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 const trustedUrl = trustedMetaGraphUrl(url, this.apiVersion);\n return this.rateLimiter.execute(async () => {\n logger.debug(\"meta\", `fetchUrl: ${trustedUrl.origin}${trustedUrl.pathname}`);\n const response = await fetch(trustedUrl, {\n headers: { Authorization: `Bearer ${this.accessToken}` },\n signal: AbortSignal.timeout(META_REQUEST_TIMEOUT_MS),\n 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 headers: {\n \"Content-Type\": \"application/json\",\n },\n signal: AbortSignal.timeout(META_REQUEST_TIMEOUT_MS),\n redirect: \"error\"});\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 let pageCount = 1;\n const expectedPathname = `/${this.apiVersion}/me/adaccounts`;\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.slice(0, MAX_PAGINATION_ROWS));\n nextUrl = firstResponse.paging?.next;\n\n // Paginate through all accounts\n while (\n nextUrl\n && pageCount < MAX_PAGINATION_PAGES\n && allAccounts.length < MAX_PAGINATION_ROWS\n ) {\n const paginatedData = await this.fetchPaginatedUrl<\n MetaAdAccountsResponse & { error?: MetaApiError }\n >(nextUrl, expectedPathname);\n\n if (paginatedData.error) {\n throw new MetaApiException(paginatedData.error);\n }\n\n const remainingRows = MAX_PAGINATION_ROWS - allAccounts.length;\n allAccounts.push(...paginatedData.data.slice(0, remainingRows));\n nextUrl = paginatedData.paging?.next;\n pageCount += 1;\n }\n\n if (nextUrl) {\n throw new Error(\"Meta ad-account pagination exceeded its page or row safety cap.\");\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 let pageCount = 1;\n const expectedPathname = `/${this.apiVersion}/${accountId}/insights`;\n\n // Build initial params\n const params: Record<string, string> = {\n fields: apiRequest.fields.join(\",\"),\n level: apiRequest.level,\n limit: String(Math.min(Math.max(apiRequest.limit || DEFAULT_LIMIT, 1), MAX_PAGE_ROWS)),\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 // Omit windows unless explicitly requested; never silently request five windows.\n Object.assign(params, measurementParams(apiRequest));\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.slice(0, MAX_PAGINATION_ROWS).map(normalizeInsightRow));\n nextUrl = firstResponse.paging?.next;\n\n // Paginate through all results\n while (\n nextUrl\n && pageCount < MAX_PAGINATION_PAGES\n && allRows.length < MAX_PAGINATION_ROWS\n ) {\n const data = await this.fetchPaginatedUrl<\n InsightsApiResponse & { error?: MetaApiError }\n >(nextUrl, expectedPathname);\n\n if (data.error) {\n throw new MetaApiException(data.error);\n }\n\n const remainingRows = MAX_PAGINATION_ROWS - allRows.length;\n allRows.push(...data.data.slice(0, remainingRows).map(normalizeInsightRow));\n nextUrl = data.paging?.next;\n pageCount += 1;\n }\n\n if (nextUrl) {\n throw new Error(\"Meta insights pagination exceeded its page or row safety cap.\");\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 let pageCount = 1;\n const expectedPathname = `/${this.apiVersion}/${reportRunId}/insights`;\n\n const firstResponse = await this.request<InsightsApiResponse>(\n `/${reportRunId}/insights`,\n { limit: \"500\" },\n );\n\n allRows.push(...firstResponse.data.slice(0, MAX_PAGINATION_ROWS));\n nextUrl = firstResponse.paging?.next;\n\n while (\n nextUrl\n && pageCount < MAX_PAGINATION_PAGES\n && allRows.length < MAX_PAGINATION_ROWS\n ) {\n const data = await this.fetchPaginatedUrl<\n InsightsApiResponse & { error?: MetaApiError }\n >(nextUrl, expectedPathname);\n\n if (data.error) {\n throw new MetaApiException(data.error);\n }\n\n const remainingRows = MAX_PAGINATION_ROWS - allRows.length;\n allRows.push(...data.data.slice(0, remainingRows));\n nextUrl = data.paging?.next;\n pageCount += 1;\n }\n\n if (nextUrl) {\n throw new Error(\"Meta async-report pagination exceeded its page or row safety cap.\");\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 let remainingPageBudget = MAX_PAGINATION_PAGES;\n\n // Helper to paginate through an ad_studies edge\n const fetchFromEdge = async (endpoint: string) => {\n try {\n if (remainingPageBudget < 1 || allStudies.length >= MAX_PAGINATION_ROWS) return;\n remainingPageBudget -= 1;\n const firstResponse = await this.request<AdStudiesResponse>(\n endpoint,\n { fields, limit: \"100\" },\n );\n\n for (const study of firstResponse.data.slice(0, MAX_PAGINATION_ROWS - allStudies.length)) {\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 const expectedPathname = `/${this.apiVersion}${endpoint}`;\n while (\n nextUrl\n && remainingPageBudget > 0\n && allStudies.length < MAX_PAGINATION_ROWS\n ) {\n remainingPageBudget -= 1;\n const data = await this.fetchPaginatedUrl<\n AdStudiesResponse & { error?: MetaApiError }\n >(nextUrl, expectedPathname);\n if (data.error) break; // Don't throw, just stop paginating\n for (const study of data.data.slice(0, MAX_PAGINATION_ROWS - allStudies.length)) {\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 let pageCount = 1;\n const expectedPathname = `/${this.apiVersion}/me/accounts`;\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 || []).slice(0, MAX_PAGINATION_ROWS)) {\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 (\n nextUrl\n && pageCount < MAX_PAGINATION_PAGES\n && pages.length < MAX_PAGINATION_ROWS\n ) {\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, expectedPathname);\n if (data.error) break;\n for (const p of (data.data || []).slice(0, MAX_PAGINATION_ROWS - pages.length)) {\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 pageCount += 1;\n }\n\n if (nextUrl) {\n throw new Error(\"Meta managed-page pagination exceeded its page or row safety cap.\");\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 = Math.min(Math.max(options.limit ?? 50, 1), MAX_SEARCH_ENTITY_ROWS);\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 let pageCount = 1;\n const expectedPathname = `/${this.apiVersion}/${formattedId}/${edge}`;\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 && pageCount < MAX_PAGINATION_PAGES) {\n const data = await this.fetchPaginatedUrl<{\n data: Array<Record<string, unknown>>;\n paging?: { next?: string };\n error?: MetaApiError;\n }>(nextUrl, expectedPathname);\n if (data.error) break;\n results.push(...(data.data || []));\n nextUrl = data.paging?.next || null;\n pageCount += 1;\n }\n\n if (nextUrl && results.length < limit && pageCount >= MAX_PAGINATION_PAGES) {\n throw new Error(\"Meta pagination exceeded the 20-page safety cap.\");\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, expectedPathname: string): Promise<T> {\n const trustedUrl = trustedMetaGraphUrl(url, this.apiVersion, expectedPathname);\n return this.rateLimiter.execute(async () => {\n const response = await fetch(trustedUrl, {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n signal: AbortSignal.timeout(META_REQUEST_TIMEOUT_MS),\n redirect: \"error\"});\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","/** Copyright 2026 GetMCPAds. SPDX-License-Identifier: Apache-2.0 */\nimport { MetaClient, MetaApiException } from \"./client.js\";\n\ntype Row = Record<string, any>;\nexport type BusinessAssetArgs = {\n businessId?: string;\n adAccountId?: string;\n limit?: number;\n cursor?: string;\n cursors?: Record<string, string>;\n maxPages?: number;\n};\nconst record = (x: unknown): x is Row =>\n !!x && typeof x === \"object\" && !Array.isArray(x);\n\n/** Each cursor belongs to one edge. Failed reads never imply an empty inventory. */\nexport async function getBusinessAssets(\n client: MetaClient,\n args: BusinessAssetArgs,\n) {\n const limit = args.limit ?? 100,\n maxPages = args.maxPages ?? 3;\n if (args.cursor && (args.businessId || args.adAccountId))\n throw Error(\n \"Use per-edge cursors for scoped Business discovery; cursor is only for /me/businesses.\",\n );\n const warnings: Row[] = [],\n coverage: Record<string, Row> = {},\n paging: Record<string, Row> = {};\n const stores = {\n businesses: new Map<string, Row>(),\n pages: new Map<string, Row>(),\n instagram_accounts: new Map<string, Row>(),\n pixels: new Map<string, Row>(),\n datasets: new Map<string, Row>(),\n };\n let requests = 0;\n let account: Row | null = null;\n const url = (path: string, params: Row) => {\n const u = new URL(\n `https://graph.facebook.com/${client.apiVersion}/${path}`,\n );\n for (const [k, v] of Object.entries(params))\n if (v !== undefined) u.searchParams.set(k, String(v));\n return u.toString();\n };\n const errorInfo = (e: unknown) => ({\n message: e instanceof Error ? e.message : String(e),\n ...(e instanceof MetaApiException\n ? { code: e.code, subcode: e.subcode }\n : {}),\n });\n const get = async (path: string, params: Row) => {\n requests++;\n const r = await client.fetchUrl(url(path, params));\n if (record(r.error)) throw new MetaApiException(r.error as any);\n return r;\n };\n const add = (\n kind: keyof typeof stores,\n items: Row[],\n source: string,\n context: Row = {},\n ) => {\n for (const item of items) {\n if (!item.id) continue;\n const id = String(item.id),\n old = stores[kind].get(id);\n stores[kind].set(id, {\n ...old,\n ...item,\n ...context,\n source,\n relationships: [\n ...new Map(\n [...(old?.relationships ?? []), { source, ...context }].map((r) => [\n JSON.stringify(r),\n r,\n ]),\n ).values(),\n ],\n sources: [...new Set([...(old?.sources ?? []), source])],\n });\n }\n };\n const node = async (path: string, fields: string) => {\n try {\n const r = await get(path, { fields });\n coverage[path] = { status: \"complete\", returnedCount: 1 };\n return r;\n } catch (e) {\n const error = errorInfo(e);\n coverage[path] = { status: \"unavailable\", returnedCount: null, error };\n warnings.push({\n area: path,\n ...error,\n suggestion:\n \"Verify access to this exact asset; an unavailable read is not an empty result.\",\n });\n return null;\n }\n };\n const edge = async (\n path: string,\n fields: string,\n fallbackFields?: string,\n ) => {\n const rows: Row[] = [];\n let after =\n args.cursors?.[path] ??\n (path === \"me/businesses\" ? args.cursor : undefined);\n let pages = 0;\n try {\n for (; pages < maxPages;) {\n let r: Row;\n try {\n r = await get(path, { fields, limit, after });\n } catch (e) {\n if (\n !fallbackFields ||\n pages !== 0 ||\n !(e instanceof MetaApiException) ||\n ![10, 100, 200].includes(e.code)\n )\n throw e;\n const area = `${path}:instagram_links`;\n const error = errorInfo(e);\n coverage[area] = {\n status: \"unavailable\",\n returnedCount: null,\n error,\n };\n warnings.push({\n area,\n ...error,\n suggestion:\n \"Page identity is retried without Instagram expansions. Missing Instagram links are unavailable, not proof that no account is linked.\",\n });\n fields = fallbackFields;\n fallbackFields = undefined;\n r = await get(path, { fields, limit, after });\n }\n if (!Array.isArray(r.data))\n throw Error(\"Meta did not return a list for this edge.\");\n rows.push(...r.data.filter(record));\n pages++;\n const pg = record(r.paging) ? r.paging : {};\n const hasMore = typeof pg.next === \"string\" && pg.next.length > 0;\n after =\n record(pg.cursors) && typeof pg.cursors.after === \"string\"\n ? pg.cursors.after\n : undefined;\n if (hasMore && !after) {\n const next = new URL(String(pg.next));\n if (\n next.origin !==\n new URL(`https://graph.facebook.com/${client.apiVersion}`)\n .origin ||\n next.pathname !== new URL(url(path, {})).pathname\n )\n throw Error(\"Untrusted pagination route.\");\n after = next.searchParams.get(\"after\") ?? undefined;\n }\n if (!hasMore) {\n coverage[path] = {\n status: rows.length ? \"complete\" : \"empty\",\n returnedCount: rows.length,\n pagesFetched: pages,\n hasMore: false,\n };\n return rows;\n }\n if (!after)\n throw Error(\n \"Meta indicated more results without a usable edge cursor.\",\n );\n }\n coverage[path] = {\n status: \"partial\",\n returnedCount: rows.length,\n pagesFetched: pages,\n hasMore: true,\n };\n paging[path] = {\n after,\n resume: {\n ...args,\n cursor: undefined,\n cursors: { ...args.cursors, [path]: after },\n },\n };\n } catch (e) {\n const error = errorInfo(e);\n coverage[path] = {\n status: rows.length ? \"partial\" : \"unavailable\",\n returnedCount: rows.length || null,\n pagesFetched: pages,\n hasMore: null,\n error,\n };\n warnings.push({\n area: path,\n ...error,\n suggestion:\n \"Check this edge and its asset permissions. Missing data is not zero; inspect status and returnedCount for this edge.\",\n });\n }\n return rows;\n };\n if (args.adAccountId) {\n const act = args.adAccountId.replace(/^act_/, \"\");\n if (!/^\\d+$/.test(act)) throw Error(\"Invalid ad account ID\");\n account = await node(\n `act_${act}`,\n \"id,name,business{id,name,verification_status}\",\n );\n }\n const owner = record(account?.business) ? account.business : null;\n if (args.businessId && owner && String(owner.id) !== args.businessId)\n throw Error(\n \"Provided Business does not own the selected ad account. No unrelated Business was queried.\",\n );\n const businessId =\n args.businessId ?? (owner?.id ? String(owner.id) : undefined);\n if (businessId) {\n if (!/^\\d+$/.test(businessId)) throw Error(\"Invalid Business ID\");\n const b = await node(businessId, \"id,name,verification_status\");\n if (b) add(\"businesses\", [b], businessId);\n else if (owner) add(\"businesses\", [owner], \"ad_account.business\");\n } else if (!args.adAccountId)\n add(\n \"businesses\",\n await edge(\"me/businesses\", \"id,name,verification_status\"),\n \"me/businesses\",\n );\n else\n warnings.push({\n area: \"ad_account.business\",\n message:\n \"The selected account did not return its owning Business. Discovery was not broadened to unrelated Businesses.\",\n suggestion: \"Verify access or supply the intended businessId explicitly.\",\n });\n if (args.adAccountId && account)\n add(\n \"pixels\",\n await edge(`${account.id}/adspixels`, \"id,name,last_fired_time\"),\n `${account.id}/adspixels`,\n { ad_account_id: account.id },\n );\n // Bound expansion of a broad /me/businesses discovery. Return explicit continuation scope.\n const bs = [...stores.businesses.values()];\n if (bs.length > 5)\n warnings.push({\n area: \"business_expansion\",\n message: \"Assets are expanded for the first five Businesses only.\",\n suggestion: \"Repeat with businessId for any other listed Business.\",\n });\n for (const b of bs.slice(0, 5)) {\n const id = String(b.id);\n for (const relation of [\"owned_pages\", \"client_pages\"]) {\n const path = `${id}/${relation}`;\n const pages = await edge(\n path,\n \"id,name,category,tasks,instagram_business_account{id,username,name},connected_instagram_account{id,username,name}\",\n \"id,name,category,tasks\",\n );\n add(\"pages\", pages, path, { business_id: id });\n for (const page of pages)\n for (const field of [\n \"instagram_business_account\",\n \"connected_instagram_account\",\n ])\n if (record(page[field]))\n add(\"instagram_accounts\", [page[field]], `${path}:${field}`, {\n business_id: id,\n page_id: page.id,\n });\n }\n add(\n \"instagram_accounts\",\n await edge(`${id}/owned_instagram_accounts`, \"id,username,name\"),\n `${id}/owned_instagram_accounts`,\n { business_id: id },\n );\n // Business assets have a different ID space from IG professional account nodes.\n const igAssets = await edge(\n `${id}/client_instagram_assets`,\n \"id,ig_user_id,ig_username\",\n );\n for (const a of igAssets)\n if (a.ig_user_id)\n add(\n \"instagram_accounts\",\n [\n {\n id: String(a.ig_user_id),\n username: a.ig_username,\n business_asset_id: a.id,\n },\n ],\n `${id}/client_instagram_assets`,\n { business_id: id },\n );\n for (const relation of [\"owned_pixels\", \"client_pixels\"])\n add(\n \"pixels\",\n await edge(`${id}/${relation}`, \"id,name,last_fired_time\"),\n `${id}/${relation}`,\n { business_id: id },\n );\n add(\n \"datasets\",\n await edge(`${id}/ads_dataset`, \"id,name\"),\n `${id}/ads_dataset`,\n { business_id: id },\n );\n }\n const unavailable = Object.entries(coverage)\n .filter(([, c]) => c.status === \"unavailable\" || c.status === \"partial\")\n .map(([edge]) => edge);\n const datasetsQueried = Object.entries(coverage).filter(([p]) =>\n p.endsWith(\"/ads_dataset\"),\n );\n return {\n scope: {\n mode: args.adAccountId\n ? \"ad_account\"\n : args.businessId\n ? \"business\"\n : \"accessible_businesses\",\n requested_ad_account_id: args.adAccountId ?? null,\n requested_business_id: args.businessId ?? null,\n },\n ad_account: account,\n account_business: owner,\n ...Object.fromEntries(\n Object.entries(stores).map(([k, v]) => [k, [...v.values()]]),\n ),\n counts: {\n ...Object.fromEntries(\n Object.entries(stores).map(([k, v]) => [k, v.size]),\n ),\n datasets:\n !datasetsQueried.length ||\n datasetsQueried.every(([, c]) => c.status === \"unavailable\")\n ? null\n : stores.datasets.size,\n },\n coverage,\n paging,\n warnings,\n limitations: [\n ...unavailable.map((edge) => ({ edge, status: coverage[edge].status })),\n ...(bs.length > 5\n ? [{ area: \"business_expansion\", status: \"partial\" }]\n : []),\n ],\n notes: [\n \"Counts describe returned records, not the size of inaccessible inventories. Inspect per-edge coverage.\",\n \"Source edges show owned/client/linked relationships; presence in a Business inventory does not prove assignment to the selected ad account.\",\n \"Page tasks and linked Instagram references do not prove that every API operation is authorized. Use relationships to retain all observed Business/Page associations for deduplicated assets.\",\n \"Dataset discovery uses Business ads_dataset; it is not the removed offline_conversion_data_sets edge.\",\n ],\n nextActions: Object.entries(paging).map(([edge, p]) => ({\n edge,\n tool: \"meta_get_business_assets\",\n arguments: p.resume,\n })),\n debug: { requestCount: requests },\n };\n}\n","/** Copyright 2026 GetMCPAds. SPDX-License-Identifier: Apache-2.0 */\n/** Graph v26 media insights. Keep format selection explicit; never send the\n * removed engagement/video_views/plays combination to every media type.\n * Native reference: https://www.postman.com/meta/instagram/folder/23987686-f659d7d1-d74c-44e4-9192-9b1e8694c511\n */\nexport function instagramInsightMetrics(media: Record<string, unknown>): string[] {\n const type = media.media_type;\n const product = media.media_product_type;\n if (product === \"STORY\") return [\"views\", \"reach\"];\n const feed = [\"views\", \"reach\", \"likes\", \"comments\", \"saved\", \"shares\", \"total_interactions\"];\n if (product === \"REELS\" && type === \"VIDEO\") {\n return [...feed, \"ig_reels_video_view_total_time\", \"ig_reels_avg_watch_time\"];\n }\n if (type === \"IMAGE\" || type === \"CAROUSEL_ALBUM\") return feed;\n // Legacy feed videos are not Reels. Their interaction/watch-time metrics\n // are not interchangeable. Unknown formats get the smallest useful read.\n return [\"reach\"];\n}\n","/** Copyright 2026 GetMCPAds. SPDX-License-Identifier: Apache-2.0 */\ntype Row = Record<string, unknown>;\nconst obj = (v: unknown): Row => v && typeof v === \"object\" && !Array.isArray(v) ? v as Row : {};\nconst rows = (v: unknown): Row[] => Array.isArray(v) ? v.map(obj) : [];\nconst str = (...vs: unknown[]): string | undefined => vs.find(v => typeof v === \"string\" && v.length > 0 && !v.includes(\"{{\")) as string | undefined;\nexport type MetaMedia = { media_id: string; image_hash?: string; image_url?: string; thumbnail_url?: string; video_id?: string; source?: string };\n\n/** Facebook permalinks are HTML pages, not files consumable by a model or player. */\nexport function metaImageFileUrl(...values: unknown[]): string | undefined {\n for (const value of values) {\n if (typeof value !== \"string\" || !value || value.includes(\"{{\")) continue;\n try {\n const url = new URL(value);\n if (!/^https?:$/.test(url.protocol)) continue;\n if (/(^|\\.)(facebook\\.com|fb\\.com)$/.test(url.hostname)) continue;\n return value;\n } catch { /* Missing/non-URL metadata is not a file. */ }\n }\n}\n\n/** Only advertiser-supplied media. A catalog thumbnail is not a creative cover.\n * Keep the complete raw creative separately for reporting/landing-page callers. */\nexport function metaCreativeMedia(creative: Row): {format: string; media: MetaMedia[]} {\n const story = obj(creative.object_story_spec), feed = obj(creative.asset_feed_spec);\n const link = obj(story.link_data), video = obj(story.video_data), photo = obj(story.photo_data), template = obj(story.template_data);\n const catalog = Boolean(creative.product_set_id || link.product_set_id || template.product_set_id || feed.product_set_id || Object.keys(template).length);\n const media: MetaMedia[] = [];\n const add = (entry: Row, fallback?: Row) => {\n const video_id = str(entry.video_id);\n const image_hash = str(entry.image_hash, entry.hash);\n const image_url = metaImageFileUrl(entry.image_url, entry.picture, entry.url);\n if (!video_id && !image_hash && !image_url) return;\n media.push({media_id: `${video_id ? 'video' : 'image'}:${video_id ?? image_hash ?? image_url}`,video_id,image_hash,image_url,\n thumbnail_url: metaImageFileUrl(entry.thumbnail_url, image_url, fallback?.thumbnail_url),source:str(entry.source)});\n };\n const children = rows(link.child_attachments);\n if (catalog) {\n // Collection cover lives in video_data/link_data or explicit template media.\n // Never substitute creative.thumbnail_url: Meta can choose a catalog product.\n add(video); add(link); add(template);\n } else if (children.length) {\n children.forEach(child => add(child));\n } else {\n add(video,creative); add(photo,creative); add(link,creative);\n }\n if (!catalog) {\n rows(feed.images).forEach(image => add(image));\n rows(feed.videos).forEach(item => add(item));\n if (!media.length) {\n add(creative);\n if (!media.length && metaImageFileUrl(creative.thumbnail_url)) media.push({media_id:String(creative.id ?? 'preview'),thumbnail_url:metaImageFileUrl(creative.thumbnail_url)});\n }\n }\n const unique = [...new Map(media.map(item => [item.media_id,item])).values()];\n return {format: catalog ? (unique.length ? \"collection\" : \"catalog_dpa\") : children.length ? \"carousel\" : unique.length > 1 ? \"flexible\" : \"single\", media: unique};\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_get_adset_configuration\",\n \"meta_get_catalog_batch_status\",\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 \"meta_list_ad_images\",\n \"meta_list_ad_videos\",\n \"meta_get_video_sources\",\n \"meta_get_entity_configuration\",\n \"meta_get_uploaded_video\",\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. Catalog mutations require explicit write opt-in, the corresponding permission and confirmation.\",\n },\n ],\n write_scopes_to_avoid_for_this_server: [\n ...(!enableWrites ? [\"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 enableWrites ? \"Write tools are enabled; each mutation requires confirm:true after preview.\" : \"Write tools are disabled on this instance.\",\n ],\n },\n ));\n}\n","/** Copyright 2026 GetMCPAds. SPDX-License-Identifier: Apache-2.0 */\nimport { z } from \"zod\";\nimport type { ToolShape } from \"../../tool-quality.js\";\nimport { toMicroCurrency, toMicros, toMinorUnits, toPlainAmount } from \"../../core/money.js\";\nconst ADS_BASE = \"https://googleads.googleapis.com/v25\";\nimport { registerMetaExtendedWrites, verifyMetaWriteScope } from \"./extended-writes.js\";\ntype Handler = (args: Record<string, unknown>) => Promise<unknown>;\ntype Collector = { tool: (n: string, d: string, s: ToolShape, h: Handler) => void };\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 * Toute écriture est un aperçu tant que `confirm` n'est pas vrai.\n *\n * C'est un assistant qui compose ces appels : il peut se tromper de compte, de\n * campagne ou d'ordre de grandeur sur un budget. Un aperçu obligatoire rend\n * l'erreur visible avant qu'elle ne coûte de l'argent, et donne à l'humain le\n * point d'arrêt que le protocole ne garantit pas.\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\nasync function request(url: string, init: RequestInit, contexte: string): Promise<unknown> {\n const response = await fetch(url, { ...init, redirect: \"error\" });\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 if (url.startsWith(ADS_BASE + \"/\")) {\n const error = (parsed as { error?: { message?: string; details?: { errors?: { errorCode?: unknown; message?: string; location?: unknown }[] }[] } })?.error;\n const details = error?.details?.flatMap(d => d.errors ?? []).map(e => ({ code: e.errorCode, message: e.message, location: e.location }));\n throw new Error(`${contexte}: ${JSON.stringify(details?.length ? details : error?.message ?? parsed).slice(0, 3000)}`);\n }\n throw new Error(`${contexte} : ${typeof parsed === \"string\" ? parsed.slice(0, 300) : JSON.stringify(parsed).slice(0, 300)}`);\n }\n return parsed;\n}\n\nexport function registerMetaWrites(c: Collector, config: Record<string, string>): void {\n registerMetaExtendedWrites(c, config);\n const destination = c;\n c = { tool(n,d,s,h) { destination.tool(n,d,s,async raw => {\n try { const a=z.object(s).strict().parse(raw); if(a.confirm) await verifyMetaWriteScope(config,a); return await h(a); }\n catch(e) { return ko((e as Error).message); }\n }); } };\n const version = config.apiVersion || \"v26.0\";\n const graph = (path: string) => `https://graph.facebook.com/${version}/${path}`;\n\n const statusTool = (nom: string, target: \"campaign\" | \"ad set\" | \"ad\", param: string) =>\n c.tool(\n nom,\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) => {\n const id = String(a[param]);\n const { status, confirm } = a as Record<string, string | boolean>;\n if (!confirm) return preview(nom, { target: id, newStatus: status });\n const result = await request(\n graph(id),\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ status: String(status), access_token: config.accessToken }),\n },\n `Meta ${target} status`,\n );\n return ok({ applied: true, action: nom, result: 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\n\n\n\n const renameTool = (nom: string, target: \"campaign\" | \"ad set\" | \"ad\", param: string) =>\n c.tool(\n nom,\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) => {\n const id = String(a[param]);\n const { name, confirm } = a as Record<string, string | boolean>;\n if (!confirm) return preview(nom, { target: id, newName: name });\n const result = await request(\n graph(id),\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ name: String(name), access_token: config.accessToken }),\n },\n `Meta ${target} rename`,\n );\n return ok({ applied: true, action: nom, 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\n\n\n}\n\n// ────────────────────────────── TikTok ──────────────────────────────\n","import { registerMetaExtendedWrites } from \"./extended-writes.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 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 registerMetaExtendedWrites(server as never, config as never, true);\n registerMetaResources(server, config.enableWrites ?? false);\n logger.info(\"meta\", \"Registered 41 read tools and 7 resources\");\n\n if (config.enableWrites) {\n registerMetaWrites(server as never, config as never);\n logger.info(\"meta\", \"Registered 23 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 { 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 */\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 { 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"],"mappings":"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","names":["name","z","amount","currency","amount","currency","z","account","request","mode","rows","z","record","obj","accountId","mergeResults","request","rows","id","rows","act","edge","obj","request","z","z","ok","preview","z","datePresetSchema","id","record","name","request","result","z","ok","z","id","name","z"]}