Skip to content

Data tools

These are the read-only MCP tools your agent uses to answer analysis questions directly. Each one is built for a specific kind of question, and returns a shape an agent can reason about without you having to name tables or columns yourself. When a question doesn’t fit any of them, your agent can fall back to the open-ended Query tool.

You don’t call these tools yourself. You ask your connected agent a question in plain language, and it decides which tool (or tools) to call. The example ask under each tool is the kind of thing you’d actually type.

Every tool below except list_brands and get_portfolio takes an optional brand argument: a brand’s name or id.

  • If you can reach exactly one brand, it’s applied automatically and you can leave it out.
  • If you can reach more than one, you must pass it, or the tool returns an error listing the brands you can reach.

See Connect your agent for the full explanation of how brand access works.

What it answers: Which brands can I query, and what are their ids?

Parameters: none.

Notable return fields:

  • brands: a list of {id, name} pairs, one per brand you can reach. If a brand’s display name can’t be resolved, name falls back to its id.

Example ask: “List the brands I can access.”

What it answers: Spend, revenue, and efficiency metrics for accounts, campaigns, ads, products, and more, over a date range, optionally against the prior period. This is the tool your agent reaches for most.

Parameters:

| Name | Type | Default | Notes | |---|---|---|---| | platform | string | "all" | all, meta, google, klaviyo, or shopify. | | entity_type | string | "account" | account, campaign, adset, ad, flow, email_campaign, search_term, keyword, product, product_group, or asset_group. Not every platform supports every type; see notes below. | | date_range | object | last 7 days, ending yesterday | {"start": "YYYY-MM-DD", "end": "YYYY-MM-DD"} or {"days": N}. | | filters | object | none | Meta adset/ad only: campaign_id, campaign_name, adset_id, ad_id, name_contains (case-insensitive substring). Ignored for any other combination. | | metrics | list | all metrics | Narrows output to these metric keys; identity fields (id, name, status, and so on) always stay. Names absent from every row come back in ignored_metrics. An empty list returns identity fields only. | | limit | integer | 50 | Top N rows by spend (or revenue/cost, depending on entity). No “more rows” indicator; lower-ranked rows are dropped silently. | | compare_to_previous | boolean | false | Adds a vs_previous block per row, against the equal-length prior window. Only applies to account, campaign, and product. | | sales_channels | list | brand’s saved policy | Shopify source_name allowlist, e.g. ["web", "pos"]. Only valid for entity_type account/product with platform all/shopify. Overrides the brand’s saved exclusion policy for this call only. | | brand | string | auto-selected if you reach one brand | Brand name or id. |

Notes on entity_type support:

  • adset and ad cover Meta and Google, but platform="all" only returns Meta rows for these two types. Query platform="google" separately for Google ad sets and ads.
  • search_term, keyword, product_group, and asset_group are Google Ads only (asset_group = Performance Max, product_group = Shopping).
  • product is Shopify only: one row per product, revenue net of discounts and refunds, no shipping (there’s no defensible way to allocate shipping per product).

Notable return fields:

  • revenue_kind is on account rows (one per row) and, for product, once at the top level of the result as store_net_merchandise (every product row shares that basis). Campaign, ad set, ad, flow, email campaign, and Google-only entity types don’t carry the field individually: their revenue is always platform-attributed last-click, per the note above.
  • With platform="all" and entity_type="account", a summary block adds a blended mer: store-net revenue over total paid spend for the full window, independent of limit.
  • Rate metrics (roas, cpa, ctr, cpc, cpm, conversion_rate, open_rate, click_rate, and others) are 0-1 fractions everywhere, and are omitted, not returned as 0, when their denominator is 0.
  • cpa’s denominator changes by level: Shopify orders at the account level, platform conversions at the campaign/adset/ad level. Don’t compare cpa across levels.

Example ask: “How did my paid channels perform over the last 7 days, compared to the week before?“

What it answers: Lists of campaigns, ad sets, ads, flows, lists, segments, or products, with their structural fields (name, status, budget, trigger, and so on) rather than performance metrics. Optionally attaches a fixed 7-day performance rollup.

Supported combinations:

  • Meta: campaign, adset, ad
  • Google: campaign
  • Klaviyo: campaign, flow, list, segment
  • Shopify: product

Parameters:

| Name | Type | Default | Notes | |---|---|---|---| | platform | string | required | meta, google, klaviyo, or shopify. | | entity_type | string | required | Must be one of the supported combinations above for the chosen platform. | | filters | object | none | Equality filters, e.g. {"status": "ACTIVE"}. Keys are validated against that entity type’s real columns; an unrecognized key returns a valid_filters error instead of an unfiltered result. | | include | list | none | ["metrics_7d"] attaches a 7-day performance rollup, ending yesterday. Only for entity_type="campaign" (any platform) or Klaviyo entity_type="flow". | | limit | integer | 50 | See ordering and total below. | | brand | string | auto-selected if you reach one brand | Brand name or id. |

Notable return fields (per entity type, beyond id, name, and status):

  • Meta campaign: objective, effective_status, budgets, buying_type.
  • Meta ad set: campaign_id, effective_status, budgets, bid_amount, bid_strategy, optimization_goal, start/end times.
  • Meta ad: campaign_id, adset_id, effective_status.
  • Google campaign: channel_type, channel_sub_type, serving_status, is_removed. Google campaign budgets and bid strategies aren’t synced, so they never appear here. serving_status is raw Google API passthrough - its enum has no REMOVED member, since serving eligibility and campaign lifecycle are different Google dimensions. It’s omitted entirely when is_removed is true, rather than returning a value that contradicts it; treat status/is_removed as authoritative for lifecycle.
  • Klaviyo campaign: subject line, preview text, sender, send strategy and time, tags.
  • Klaviyo flow: trigger_type, tags.
  • Klaviyo list/segment: profile_count and related counts.
  • Shopify product: uses title instead of name, the only entity type with this naming quirk.
  • With include=["metrics_7d"] on a Klaviyo campaign or flow, the rollup’s open and click rates are 0-100 percentages, an exception to the 0-1 convention get_performance uses everywhere else.

Meta budgets (campaign vs. ad set): a campaign missing daily_budget/lifetime_budget while its ad sets carry them means Ad Set Budget Optimization (ABO) - read the budget off the ad sets. A campaign carrying a budget while its ad sets don’t means Campaign Budget Optimization (CBO) - read it off the campaign. Neither level carrying a budget means the budget data is unavailable (for example an ended or stale budget object), not a sync gap.

Ordering and total: total is the full pre-limit match count; count is just how many rows came back, so raise limit if total exceeds count and you need completeness. For platform meta/google with entity_type="campaign", rows sort by 7-day spend descending (the same window as metrics_7d), ties broken by name ascending. Every other combination - Meta ad sets/ads, all Klaviyo entity types, and Shopify products - has no campaign-grain spend concept, so those sort by name ascending, falling back to platform_id ascending when name is null or tied.

Example ask: “List my active Meta ad sets.”

What it answers: One metric, compared across several subjects (channels or campaigns), over a single shared window. This is not period-over-period comparison; use compare_to_previous on get_performance for that.

Parameters:

| Name | Type | Default | Notes | |---|---|---|---| | subjects | list of objects | required | At least 2, or the tool returns an error. Each is {"label", "platform"?, "entity_type"?, "id"?}. entity_type defaults to account (a full channel) or is campaign (requires id, the platform’s campaign id). platform defaults to all for account subjects (blended across every connected channel); campaign subjects support meta, google, or all (scans both). | | metric | string | "revenue" | spend, revenue, roas, cpa, orders, conversions, or ctr. For campaign subjects, orders and conversions are the same value; there’s no true order count at campaign level. | | window | object | last 7 days, ending yesterday | {"days": N} or {"start", "end"}. | | sales_channels | list | brand’s saved policy | Same Shopify source_name allowlist as get_performance. Requires at least one account subject on platform all or shopify. | | brand | string | auto-selected if you reach one brand | Brand name or id. |

Notable return fields:

  • Each subject with a value carries a revenue_kind: store_net, attributed_last_click, or blended, per the note above.
  • When subjects mix revenue kinds for a revenue/roas comparison, the response adds a top-level notices entry warning the values aren’t additive.
  • A subject with no matching data returns {"label", "value": null, "reason": ...} instead of failing the whole call.
  • Campaign lookups only scan the top ~1,000 campaigns by spend; if a campaign subject doesn’t set platform, a shared id across platforms can collide.

Example ask: “Compare ROAS between Meta and Google over the last 14 days.”

What it answers: Which channels or campaigns moved outside a normal range this period, versus a baseline period.

Parameters:

| Name | Type | Default | Notes | |---|---|---|---| | window | object | last 7 days | The recent period to evaluate. | | baseline_window | object | the 14 days immediately before window | The comparison period. | | threshold | number | none | An extra minimum percent-change floor. Can only make detection stricter, never looser than the built-in thresholds. | | channels | list | all channels | Any of meta_ads, google_ads, klaviyo_email, shopify_store. Any unrecognized id - alone or mixed with valid ones - returns a valid_channels error instead of a silently filtered or unfiltered result. | | severity | list | all severities | Subset of critical/warn to include, applied before the 10-alert cap. Omit to get all severities; an explicit empty list is a structured error, not “all”. An unrecognized value returns a valid_severities error. | | brand | string | auto-selected if you reach one brand | Brand name or id. |

Notable return fields:

  • Evaluates spend, revenue, orders, ROAS, CPA, CTR, and CPM, both at the account level and for the top 20 campaigns by baseline spend. Campaign orders is a platform conversion count and can be fractional on Google.
  • Capped at the 10 highest-severity alerts; truncated says whether more matched. A window with more than 10 critical alerts can otherwise crowd out every warning - pass severity=["warn"] to retrieve them separately.
  • direction is literally up or down, not a judgment call: an “up” CPA is bad, but the field just reports which way the number moved.
  • A metric moving to or from zero is only flagged once it crosses a floor (spend: 50 account-currency units/day, orders: 5/day). Going from zero to active is labelled new_activity (no percent change is reported); going from active to zero is labelled stopped (reported as a flat -100%).
  • Revenue evidence carries revenue_kind, per the note above.
  • Every alert’s evidence carries basis: "daily_avg" for spend/revenue/orders (calendar-day averages) or "ratio" for ROAS/CPA/CTR/CPM (ratios of summed window bases, never averaged). window_value/baseline_value keep the same field names either way.

Example ask: “Did anything unusual happen with my ad performance this week?“

What it answers: Creative-level ad performance with fatigue diagnostics and AI-generated tags, for “what’s working” and “what’s fatiguing” questions.

Parameters:

| Name | Type | Default | Notes | |---|---|---|---| | platform | string | "meta" | meta or google. Google is metrics-only: only fields="core" is valid, group_by/filters are rejected, and it requires a specific (non-manager) Google Ads customer account to already be selected on the connection. An empty Google page adds empty_reason: asset_metrics_not_synced (the per-asset sync has never populated this account) vs no_rows_in_window (rows exist, just none in the window - widen date_range). | | filters | object | none | Meta only: ad_id, campaign_id, adset_id, status. Unknown keys are rejected with an error, not ignored. | | fields | string | "core" | core (identity, tag labels, metrics, fatigue), tags (adds AI-written description, hook summary, angle, opening seconds, spoken/on-screen text, and pacing), or all (adds media URLs, headline, body, call to action). | | group_by | list | none | Any of hook_type, format_type, combinable. Switches the response to a blended rollup per group instead of per creative. | | date_range | object | last 7 days, ending yesterday | {"start", "end"} or {"days": N}. Widen it to browse the full creative library, e.g. {"days": 365}. | | limit | integer | 200 | Hard ceiling of 1000 regardless of what you pass. Ignored in rollup mode. | | brand | string | auto-selected if you reach one brand | Brand name or id. |

Notable return fields:

  • Only creatives with spend in date_range come back; a creative with zero spend in the window is left out entirely.
  • fatigue: hook_rate, hold_rate (null for non-video creatives), ctr, and a trend block (first half vs. second half of the window) for CTR, hook rate, and frequency.
  • lifecycle_stage (testing, scaling, winner, fatiguing, dead) is computed over a fixed trailing 30-day window, deliberately different from date_range, so a creative can read as a 30-day winner while showing no spend in a narrower window.
  • lifecycle_reason disambiguates a dead creative (null for every other stage): paused (status not ACTIVE), underperforming (still ACTIVE and spending, but recent and cumulative ROAS both below half the efficiency bar), or dormant (delivered in date_range but absent from the 30-day window). So dead does not always mean “stopped delivering” - an underperforming creative may still be actively spending. Also null on a dead creative whose ad metadata is missing (performance survives a deleted Meta ad) - status is unknown there, so it’s never asserted as paused.
  • quality_ranking, engagement_rate_ranking, conversion_rate_ranking are Meta’s own diagnostic rankings; the key is absent when Meta hasn’t reported one yet.
  • first_delivery/age_days are measured over the full synced history, not date_range.
  • Creative revenue/roas/purchases are Meta-attributed, not Shopify-verified store revenue.
  • The response envelope includes total_creative_spend (every delivered creative matching the filter, not limited by limit) and omitted (count and spend dropped by the limit), so the two reconcile with what’s returned.

Example ask: “Show me my Meta video creatives from the last 30 days, grouped by hook type, with fatigue signals.”

What it answers: A cross-brand rollup across every brand you can reach, for “what changed across my brands this week, who needs attention” in one call. Unlike every other tool on this page, it takes no brand argument: it always covers your entire reach.

Parameters:

| Name | Type | Default | Notes | |---|---|---|---| | date_range | string | "7d" | A day-count string ending in d, e.g. "7d", "30d", "90d". Ends yesterday. | | sort_by | string | "revenue" | revenue, spend, mer, or revenue_delta. revenue_delta sorts ascending (biggest decline first) and requires compare_to_previous=true. | | limit | integer | 25 | Hard-capped at 50. Applies per workspace, not overall; brands beyond it are dropped from the list but still counted in total_brands. | | compare_to_previous | boolean | true | The opposite default from get_performance’s compare_to_previous (compare has no equivalent parameter). |

Notable return fields:

  • Rows are grouped under workspaces. If you belong to more than one workspace, you get one group per workspace; an API key always yields exactly one group.
  • Each brand row: revenue (refund-correct store-net, same basis as get_performance’s store_net), orders, aov, ad_spend, mer, connected_platforms, and data_through (the oldest per-platform freshness date, so one stale connection makes the whole brand read stale).
  • currency is per brand, with no FX conversion: comparing raw revenue across brands in different currencies compares different units.
  • A workspace-group subtotal only appears when every brand in that group shares the same currency and the same resolved revenue policy; otherwise it’s left out rather than approximated, and notices says explicitly there’s no other cross-brand total anywhere in the response.

Example ask: “Which of my brands lost the most revenue this month?“

What it answers: A deterministic, source-cited seven-day growth brief: refund-correct store revenue, refunds, paid spend, MER, paid-platform ROAS, CAC, Klaviyo attributed revenue, campaign movers, threshold anomalies, and fixed-rule next actions.

Parameters:

| Name | Type | Default | Notes | |---|---|---|---| | week_ending | string (date) | the last 7 complete reporting days | YYYY-MM-DD. Must be before today, so the brief only covers complete days. | | brand | string | required if you reach more than one brand | Brand name or id. |

Notable return fields:

  • Every figure in the brief carries a citation back to its source.
  • The brief explicitly warns that store revenue and attributed revenue are different bases and must not be added together.

Example ask: “Give me this week’s growth brief.”

Email support@kreel.ai if a tool returns something you can’t explain.