{
  "id": "compare_campaign_performance",
  "mcp_tool": "compare_campaign_performance",
  "status": "approved",
  "source_system": "cake",
  "environment": ["staging", "prod"],
  "sharded": false,
  "connections": {
    "whale": "cake_whale_ro",
    "admin": "cake_admin_ro"
  },
  "objects": [
    "whale.lucos_ro_advertiser_stats_daily",
    "Admin.lucos_ro_adv_campaign_performance_daily"
  ],
  "parameters": {
    "advertiser_id": { "type": "int", "required": false },
    "campaign_id": { "type": "int", "required": false },
    "date_from": { "type": "date", "required": false },
    "date_to": { "type": "date", "required": false },
    "compare_from": { "type": "date", "required": false },
    "compare_to": { "type": "date", "required": false },
    "compare_mode": {
      "type": "enum",
      "required": false,
      "default": "roas_underperform",
      "values": ["roas_underperform", "spend_up_conversions_down", "spend_increase"]
    },
    "roas_underperform_pct": { "type": "int", "required": false, "default": 15 },
    "spend_increase_pct": { "type": "int", "required": false, "default": 20 },
    "min_clicks": { "type": "int", "required": false },
    "sort_by": {
      "type": "enum",
      "required": false,
      "values": ["roas_drop_pct", "spend_delta_pct", "spend_delta", "spend", "roas", "clicks", "sales_revenue"]
    },
    "sort_dir": {
      "type": "enum",
      "required": false,
      "default": "desc",
      "values": ["desc", "asc"]
    },
    "limit": { "type": "int", "required": false, "default": 50 }
  },
  "constraints": {
    "max_window_days": 92,
    "default_window_days": 31,
    "max_rows": 5000,
    "timeout_ms": 60000
  },
  "allow_fields": [
    "camp_id",
    "campaign_name",
    "adv_id",
    "adv_name",
    "clicks",
    "estimated_conversions",
    "advertiser_conversions",
    "sales_revenue",
    "spend",
    "roas",
    "roas_source",
    "compare_clicks",
    "compare_estimated_conversions",
    "compare_advertiser_conversions",
    "compare_sales_revenue",
    "compare_spend",
    "baseline_roas",
    "roas_drop_pct",
    "spend_delta",
    "spend_delta_pct",
    "cpa",
    "compare_cpa",
    "cpa_delta",
    "cpa_delta_pct",
    "cpa_source"
  ],
  "deny_fields": ["aov", "rev_share", "postback_url", "ip"],
  "post_processing": [
    "Scan each window at campaign grain (GROUP BY adv_id, camp_id) up to max_rows, then merge whale spend with Admin sales_revenue in TypeScript (OD-1 — not a SQL join).",
    "Predicate runs AFTER the merge. Response LIMIT is applied last so underperformers that are not top-N spend are kept.",
    "roas_underperform: keep current_roas < baseline_roas * (1 - roas_underperform_pct/100). Both windows need spend > 0.",
    "spend_up_conversions_down: keep spend > compare_spend AND estimated_conversions < compare_estimated_conversions when compare_spend > 0 and compare_estimated_conversions > 0.",
    "spend_increase: keep compare_spend > 0 AND (spend - compare_spend) / compare_spend > spend_increase_pct/100.",
    "truncated=true when either whale scan hit max_rows. matched_count is pre-limit."
  ],
  "notes": "Period-compare sibling of get_campaign_performance_report (TW-338). advertiser_id is optional — omit it for cross-advertiser questions (historical ROAS underperform / spend-up conversions-down). Do not N× loop get_campaign_performance_report. Do not use get_advertiser_report to rank campaigns (no camp_id). Campaign CPA (spend / estimated_conversions) is emitted on matched rows. Do not use this tool for advertiser-grain average CPA last 30 vs previous 30 / this month vs last month — that is two get_advertiser_report { group_by: advertiser } calls (rolling last 30 vs previous 30, or MTD vs last_month; CPA = spend / estimated_conversions). Do not use kpi (goal, not historical). Scan cap is 5000 campaigns per window; returned rows max 50. Each window independently ≤ 92 days."
}
