Skip to main content
Glama
kLOsk

Google Ads - AdLoop

by kLOsk

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault

No arguments

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": true
}
logging
{}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}

Tools

Functions exposed to the LLM to take actions

NameDescription
health_checkA

Test AdLoop connectivity — checks OAuth token, GA4 API, Google Ads API, and (when configured) the Reddit Ads API.

Run this first if other tools are failing. Returns status for each service and actionable guidance if something is broken.

get_account_summariesA

List all GA4 accounts and properties accessible by the authenticated user.

Use this as the first step to discover which GA4 properties are available. Returns account names, property names, and property IDs.

run_ga4_reportA

Run a custom GA4 report with specified dimensions, metrics, and date range.

Common dimensions: date, pagePath, sessionSource, sessionMedium, country, deviceCategory, eventName Common metrics: sessions, totalUsers, newUsers, screenPageViews, conversions, eventCount, bounceRate

Date formats: "today", "yesterday", "7daysAgo", "28daysAgo", "90daysAgo", or "YYYY-MM-DD". If property_id is empty, uses the default from config.

run_realtime_reportA

Run a GA4 realtime report showing current active users and events.

Useful for checking if tracking is firing correctly after code changes. Common dimensions: unifiedScreenName, eventName, country, deviceCategory Common metrics: activeUsers, eventCount

get_tracking_eventsA

List all GA4 events and their volume for the given date range.

Returns every distinct event name with its total event count. Use this to understand what tracking is configured and active.

list_gsc_sitesA

List all Google Search Console properties the authenticated user can access.

Use this first to discover which site URLs are available before running search analytics reports. Returns the site URL and permission level for each property.

run_gsc_reportA

Run a Google Search Console search analytics report.

Returns clicks, impressions, CTR, and average position broken down by the requested dimensions. Useful for diagnosing organic traffic drops, finding keyword opportunities, and cross-referencing with GA4 and Ads data.

site_url: the GSC property URL (e.g. "https://example.com/" or "sc-domain:example.com"). Defaults to gsc.site_url in config.yaml. dimensions: one or more of ["query", "page", "country", "device", "date"]. Defaults to ["query"]. date_range_start / date_range_end: ISO dates (YYYY-MM-DD) or relative values like "7daysAgo", "30daysAgo", "today". search_type: "web" (default), "image", "video", "news", "discover", or "googleNews". dimension_filter_groups: optional GSC DimensionFilterGroup list to filter by query, page, country, or device. Example: [{"filters": [{"dimension": "query", "operator": "contains", "expression": "analytics"}]}] limit: maximum rows to return (default 100, max 25000).

analyze_page_speedA

Run PageSpeed Insights for a landing page — Lighthouse + real-user data.

Returns the performance score (0-100), lab Core Web Vitals (LCP, CLS, TBT, FCP), CrUX field data from real Chrome users where available (p75 LCP/INP/CLS + FAST/AVERAGE/SLOW ratings), and the top improvement opportunities with estimated savings.

Use on ad final_urls: slow landing pages depress Quality Score and waste paid clicks. strategy: "mobile" (default — most paid traffic) or "desktop". Takes 10-30s; that is normal for a Lighthouse run.

list_merchant_accountsA

List Google Merchant Center accounts the connected user can access.

Call first to discover merchant IDs for get_merchant_feed_health. Distinguishes standalone accounts from aggregator (MCA) accounts.

get_merchant_feed_healthA

Merchant Center feed health — disapproved products + account issues.

Disapproved feed items silently starve Shopping and Performance Max campaigns; this surfaces approved/pending/disapproved counts per reporting context (Shopping ads, free listings, ...), the top product issues by affected products (with documentation links), and account-level issues — CRITICAL ones stop offers serving entirely.

account_id: numeric Merchant Center ID from list_merchant_accounts. Product-status data lags reality by ~30 minutes.

list_accountsA

List accessible Google Ads accounts.

Returns account names, IDs, and status. The default cap of 200 covers the vast majority of agency MCCs in one call. If the user explicitly asked to see ALL of their accounts and the response comes back with 'truncated: true', call this tool again with a much higher limit (e.g. list_accounts(limit=1000)) — do not stop at the truncated list. For workflows that target a specific account you don't need to enumerate at all: pass customer_id directly to get_campaign_performance, run_gaql, etc.

get_campaign_performanceA

Get campaign-level performance metrics for a date range.

Returns: campaign name, status, type, impressions, clicks, cost, conversions, CPA, ROAS, CTR for each campaign. Date format: "YYYY-MM-DD". Empty = last 30 days.

Set compact=true for audits/overviews on large accounts: returns account totals, status/type breakdowns, the top-10 spenders, and zero-conversion offenders instead of every row (~90% smaller).

get_ad_performanceA

Get ad-level performance data including headlines, descriptions, and metrics.

Returns: ad type, headlines, descriptions, final URL, impressions, clicks, CTR, conversions, cost for each ad.

Set compact=true for audits/overviews: returns totals, the top-10 ads with headline/description COUNTS instead of full asset lists, plus incomplete-RSA and single-ad ad-group findings (~90% smaller).

get_keyword_performanceA

Get keyword metrics including quality scores and competitive data.

Returns: keyword text, match type, quality score, impressions, clicks, CTR, CPC, conversions for each keyword.

Set compact=true for audits/overviews: returns totals, match-type distribution, the top-10 spenders, low-quality-score keywords, and zero-conversion spenders instead of every row (~90% smaller).

get_search_termsA

Get search terms report — what users actually typed before clicking your ads.

Critical for finding negative keyword opportunities and understanding user intent. Returns: search term, campaign, ad group, impressions, clicks, conversions.

Set compact=true for audits/overviews: returns totals, the top-10 terms by clicks, ready-made negative-keyword waste candidates (5+ clicks, zero conversions), and top converters (~90% smaller).

get_negative_keywordsA

List existing negative keywords for a campaign or all campaigns.

Use this before adding negative keywords to check for duplicates. If campaign_id is empty, returns negatives across all campaigns.

get_negative_keyword_listsA

List all shared negative keyword lists (SharedSets) in the account.

Returns each list's ID, name, status, and keyword count. Always call this before propose_negative_keyword_list to avoid creating duplicates — a suitable list may already exist and just need attaching to a campaign.

get_negative_keyword_list_keywordsA

List the keywords inside a shared negative keyword list.

shared_set_id: numeric ID from get_negative_keyword_lists (shared_set.id).

get_negative_keyword_list_campaignsA

List which campaigns a shared negative keyword list is attached to.

shared_set_id: numeric ID from get_negative_keyword_lists. Omit to see all list-to-campaign attachments across the account.

get_recommendationsA

Retrieve Google's auto-generated recommendations with estimated impact.

Returns each recommendation's type, associated campaign/ad group, current (base) and projected (potential) metrics, and the estimated improvement.

recommendation_types: optional filter — e.g. ["KEYWORD", "TARGET_CPA_OPT_IN", "MAXIMIZE_CONVERSIONS_OPT_IN", "RESPONSIVE_SEARCH_AD"]. Empty = all types. campaign_id: optional — scope to a single campaign.

Includes insights that flag budget-increase recommendations (often self-serving) and highlight high-impact suggestions worth investigating.

get_pmax_performanceA

Get Performance Max campaign and asset group performance.

Returns two result sets:

  • campaigns: PMax campaign metrics broken down by ad_network_type (SEARCH, CONTENT, YOUTUBE_SEARCH, YOUTUBE_WATCH, MIXED). Note: MIXED is a catch-all that Google uses for most PMax traffic — full channel splits are not available via the API.

  • asset_groups: per-asset-group metrics including ad_strength (EXCELLENT, GOOD, AVERAGE, POOR).

Includes insights flagging weak ad strength, zero-conversion asset groups, and network type distribution. Date format: "YYYY-MM-DD". Empty = last 30 days.

get_asset_performanceA

Get per-asset details for Performance Max campaigns.

Returns each asset's field_type (HEADLINE, DESCRIPTION, MARKETING_IMAGE, YOUTUBE_VIDEO, etc.), primary_status (ELIGIBLE, NOT_ELIGIBLE, PAUSED, PENDING), and content (text or image URL).

Note: per-asset performance labels (BEST/GOOD/LOW) are not available for PMax assets in the Google Ads API. Use get_detailed_asset_performance to see which asset combinations Google selects most — the closest proxy for individual asset quality.

campaign_id: optional filter to a single PMax campaign. Includes by_status and by_field_type summaries.

get_detailed_asset_performanceA

Get top-performing asset combinations for Performance Max campaigns.

Shows which headline + description + image combinations Google selects most often. Each combination lists the assets used and their field types. This data helps identify which creative elements work well together.

campaign_id: optional filter to a single PMax campaign.

get_audience_performanceA

Get audience segment performance metrics.

Returns performance by audience type — remarketing lists (USER_LIST), in-market segments (USER_INTEREST), affinity, demographics (AGE_RANGE, GENDER), etc. Shows display_name, impressions, clicks, cost, conversions, CTR, and CPC for each audience.

Works for campaigns with explicit audience targeting (Search, Display). PMax audience targeting is automatic and may not appear in this report. campaign_id: optional filter to a single campaign. Date format: "YYYY-MM-DD". Empty = last 30 days.

get_demographic_targetingA

List demographic targeting criteria (age, gender, parental status, income).

Provide exactly one of ad_group_id or campaign_id. Returns each criterion's value, whether it's negative (excluded) or positive (narrowing), status, and a remove_id (composite resource ID) that can be passed directly to remove_entity with entity_type='ad_group_criterion' or 'campaign_criterion'.

By default, Google Ads serves ads to ALL demographic segments — a criterion only appears here once you've actively excluded or narrowed.

analyze_campaign_conversionsA

Campaign clicks → GA4 conversions mapping — the real cost-per-conversion.

Combines Google Ads campaign metrics with GA4 session/conversion data to reveal click-to-session ratios (GDPR indicator), compare Ads-reported vs GA4-reported conversions, and compute cost-per-GA4-conversion.

Also returns non-paid channel conversion rates for comparison context. Date format: "YYYY-MM-DD". Empty = last 30 days.

landing_page_analysisA

Analyze which landing pages convert and which don't.

Combines ad final URLs with GA4 page-level data to show paid traffic sessions, conversion rates, bounce rates, and engagement per landing page. Identifies pages that get ad clicks but zero conversions and orphaned URLs. Date format: "YYYY-MM-DD". Empty = last 30 days.

attribution_checkA

Compare Ads-reported conversions vs GA4 — find tracking discrepancies.

Checks whether conversions reported by Google Ads match what GA4 records, diagnoses GDPR consent gaps, attribution model differences, and missing conversion event configuration.

conversion_events: optional list of GA4 event names to specifically check (e.g. ["sign_up", "purchase"]). If omitted, compares aggregate totals only. Date format: "YYYY-MM-DD". Empty = last 30 days.

audit_event_coverageA

Three-way audit: codebase events ↔ GTM tags ↔ GA4 actual fires.

First, search the user's codebase for gtag('event', ...) and dataLayer.push({event: ...}) calls and extract every distinct event name. Pass that list as expected_events. The tool fetches the LIVE GTM container, joins it against GA4 event counts for the date range, and returns a per-event matrix with one of these statuses: ok — tag active and event firing ok_auto_collected — GA4 Enhanced Measurement event, no tag needed no_tag_no_fire — codebase event, no GTM tag, never fires tag_paused — GTM tag exists but is paused tag_active_but_not_firing — tag is active but no GA4 hits gtm_only_firing — GA4 event from a tag, not in codebase gtm_paused_but_firing — only paused tag(s), not in codebase, yet GA4 still fires (event comes from elsewhere) gtm_only_not_firing — tag exists, not in codebase, no fires ga4_only — fires in GA4, no tag, no codebase ref ga4_fires_no_tag — codebase event firing without a GTM tag auto_event_only — Enhanced Measurement event with no codebase ref

Also surfaces dynamic-event tags ({{Event}} variables) and Custom HTML tags that the audit cannot interpret automatically.

GTM IDs come from Tag Manager UI → Admin → Container Settings. Date format: "YYYY-MM-DD". Empty = last 30 days.

list_gtm_accountsA

List all GTM accounts the AdLoop service account / OAuth user can read.

Use this for first-time discovery before calling audit_event_coverage — you need the account_id from here. If this returns an empty list, the service account hasn't been added to any GTM container with at least Read permission.

list_gtm_containersA

List all containers under a GTM account.

Returns container_id (the numeric ID needed by audit_event_coverage), public_id (the GTM-XXXXXXX string shown in the UI), name, and usage context (web / iOS / Android / amp / server).

list_gtm_tagsA

List every tag in the LIVE GTM container.

Each tag includes type, status, parsed parameters, the GA4 event name (for GA4 event tags), and resolved firing/blocking trigger names. Use after audit_event_coverage to inspect specific tags.

get_gtm_tagA

Get the full RAW configuration for a single GTM tag.

Includes every parameter, firing/blocking triggers (with their filter conditions resolved to text), priority, pause status, sampling, and monitoring metadata. Use to inspect a tag flagged by audit_event_coverage.

list_gtm_triggersA

List every trigger in the LIVE GTM container.

Each trigger has its filter conditions parsed to readable text (e.g. "{{Page Path}} matches RegExp ^/service-promotions/"). Use to diagnose why a tag fires or doesn't fire on specific pages.

get_gtm_triggerA

Get the full RAW configuration for a single GTM trigger.

Includes filters, auto-event filters, custom-event filters, validation settings, and a list of every tag that uses this trigger. Use to diagnose why a tag with a specific trigger ID does or doesn't fire.

list_gtm_variablesA

List GTM variables — both custom and enabled built-in.

Custom variables come from the live container. Built-in variables (Page URL, Click Element, Form ID, etc.) come from the workspace's enabled-built-ins list. Variables matter because triggers reference them — if a trigger uses {{Form ID}} but Form ID isn't enabled, the trigger never matches.

list_gtm_workspacesA

List workspaces (drafts) under a GTM container.

Workspace IDs are needed for get_gtm_workspace_diff. Most containers have a single Default Workspace; multiple workspaces appear when the team uses parallel drafts.

get_gtm_workspace_diffA

Show drafted-but-not-published changes in a GTM workspace.

Returns the list of entities (tags, triggers, variables) added, modified, or deleted relative to the live published version, plus any merge conflicts. Common cause of "I edited a tag but nothing happened" — the workspace was never published. is_clean=true means no pending changes and no conflicts.

list_gtm_versionsA

List published GTM version history (newest first).

Version headers include version_id, name, and entity counts. Use to correlate a metric drop with a recent publish: fetch versions, find one with timestamps near the drop date, then call get_gtm_version for full content + author info.

get_gtm_versionA

Get full metadata + entity counts for a single GTM container version.

Returns name, description, fingerprint, and lists of tag/trigger/ variable names at that point in time. Use after list_gtm_versions when correlating a metric drop with a specific publish.

run_gaqlA

Execute an arbitrary GAQL (Google Ads Query Language) query.

Use this for advanced queries not covered by the other tools. See the GAQL reference in the AdLoop cursor rules for syntax help.

format: "table" (default, readable), "json" (structured), "csv" (exportable)

draft_campaignA

Draft a full campaign structure — returns a PREVIEW, does NOT create anything.

Creates: CampaignBudget + Campaign (PAUSED) + AdGroup + optional Keywords

  • geo targeting + language targeting. Ads are NOT included — use draft_responsive_search_ad after the campaign exists.

bidding_strategy: MAXIMIZE_CONVERSIONS | TARGET_CPA | TARGET_ROAS | MAXIMIZE_CONVERSION_VALUE | TARGET_SPEND | MANUAL_CPC target_cpa: required if bidding_strategy is TARGET_CPA (in account currency) target_roas: required if bidding_strategy is TARGET_ROAS keywords: list of {"text": "keyword", "match_type": "EXACT|PHRASE|BROAD"} search_partners_enabled: include ads on Search partners display_network_enabled: enable Search campaign display expansion display_expansion_enabled: alias for display_network_enabled max_cpc: manual CPC bid for the initial ad group when bidding_strategy is MANUAL_CPC, or the Maximize Clicks CPC cap when bidding_strategy is TARGET_SPEND geo_target_ids: REQUIRED list of geo target constant IDs Common: "2276" Germany, "2040" Austria, "2756" Switzerland, "2840" USA, "2826" UK, "2250" France. Full list: Google Ads API geo target constants. language_ids: REQUIRED list of language constant IDs Common: "1001" German, "1000" English, "1002" French, "1004" Spanish, "1014" Portuguese. Full list: Google Ads API language constants.

Call confirm_and_apply with the returned plan_id to execute.

draft_ad_groupA

Draft a new ad group within an existing campaign — returns a PREVIEW, does NOT create.

Creates an ad group (ENABLED, type SEARCH_STANDARD) in the specified campaign. Optionally includes keywords in the same atomic operation.

campaign_id: The campaign to add the ad group to (get from get_campaign_performance). ad_group_name: Name for the new ad group. keywords: Optional list of {"text": "keyword", "match_type": "EXACT|PHRASE|BROAD"}. cpc_bid_micros: Optional ad group CPC bid in micros (only for MANUAL_CPC campaigns).

Call confirm_and_apply with the returned plan_id to execute.

update_campaignA

Draft an update to an existing campaign — returns a PREVIEW, does NOT apply.

Only include the parameters you want to change. Omit the rest.

campaign_id: the numeric ID of the campaign to update (required) bidding_strategy: MAXIMIZE_CONVERSIONS | TARGET_CPA | TARGET_ROAS | MAXIMIZE_CONVERSION_VALUE | TARGET_SPEND | MANUAL_CPC target_cpa: required if bidding_strategy is TARGET_CPA (in account currency) target_roas: required if bidding_strategy is TARGET_ROAS daily_budget: new daily budget in account currency geo_target_ids: REPLACES all geo targets. Common IDs: "2276" Germany, "2040" Austria, "2756" Switzerland, "2840" USA, "2826" UK language_ids: REPLACES all language targets. Common IDs: "1001" German, "1000" English, "1002" French, "1004" Spanish search_partners_enabled: include ads on Search partners display_network_enabled: enable Search campaign display expansion display_expansion_enabled: alias for display_network_enabled max_cpc: Maximize Clicks CPC cap when bidding_strategy is TARGET_SPEND, or when the existing campaign already uses TARGET_SPEND

Call confirm_and_apply with the returned plan_id to execute.

draft_responsive_search_adA

Draft a Responsive Search Ad — returns a PREVIEW, does NOT create the ad.

Provide 3-15 headlines (max 30 chars each) and 2-4 descriptions (max 90 chars each). The preview shows exactly what will be created. Call confirm_and_apply to execute.

Each headline/description entry may be either:

  • a plain string (unpinned), or

  • a dict {"text": "...", "pinned_field": "HEADLINE_1"} (pinned).

Valid pin values: headlines: HEADLINE_1, HEADLINE_2, HEADLINE_3 descriptions: DESCRIPTION_1, DESCRIPTION_2

Google caps: at most 2 headlines per pin slot, at most 1 description per pin slot. Mixed plain-string and dict entries are allowed within a single call (e.g. brand pinned to HEADLINE_1, the rest unpinned).

update_responsive_search_adA

Update mutable fields on an existing RSA in place — returns a PREVIEW.

Edits an existing RSA without creating a new ad; the ad keeps its ID. Google Ads API v23 (AdService.MutateAds) permits in-place mutation of final_urls, path1, path2, headlines, and descriptions.

IMPORTANT: replacing headlines or descriptions is NOT a free in-place edit. Even though the ad ID is preserved, swapping the creative text RESETS the ad's asset-combination learning and performance history and sends the ad BACK THROUGH Google policy review — Google treats the creative as new for optimization. URL-only and path-only edits do not incur this. When headlines/descriptions change, the returned preview includes a warnings entry — surface it to the user before applying.

Headlines/descriptions are LIST-REPLACE — when provided, the supplied list fully swaps in for the existing one, and Google's RSA constraints apply (3-15 headlines, 2-4 descriptions, 30/90 char limits, pin-slot rules). Each entry may be a plain string (unpinned) or {"text": "...", "pinned_field": "HEADLINE_1"}.

Argument semantics: - headlines / descriptions: None or [] -> no change; non-empty list -> replaces the existing list in full - final_url: empty -> no change; non-empty -> replaces final URL - path1 / path2: empty -> no change; non-empty -> sets value - clear_path1 / clear_path2: True -> set to empty string

At least one mutation must be requested. Call confirm_and_apply with the returned plan_id to execute.

draft_keywordsA

Draft keyword additions — returns a PREVIEW, does NOT add keywords.

keywords: list of {"text": "keyword phrase", "match_type": "EXACT|PHRASE|BROAD"} Call confirm_and_apply with the returned plan_id to execute.

add_negative_keywordsA

Draft negative keyword additions — returns a PREVIEW.

Negative keywords prevent your ads from showing for irrelevant searches. match_type: "EXACT", "PHRASE", or "BROAD" Call confirm_and_apply with the returned plan_id to execute.

add_negative_locationsA

Draft negative geo location additions — returns a PREVIEW.

Use this to exclude cities/regions from a campaign while keeping broader positive targets such as State of Sao Paulo. geo_target_ids are numeric Google geo target constant IDs. Call confirm_and_apply with the returned plan_id to execute.

propose_negative_keyword_listA

Draft a shared negative keyword list and attach it to a campaign — returns a PREVIEW.

Creates a reusable negative keyword list that can later be applied to multiple campaigns, unlike add_negative_keywords which adds directly to one campaign. match_type: "EXACT", "PHRASE", or "BROAD" Call confirm_and_apply with the returned plan_id to execute.

add_to_negative_keyword_listA

Append keywords to an EXISTING shared negative keyword list — returns a PREVIEW.

Use this when a suitable list already exists and only needs more keywords (instead of propose_negative_keyword_list, which creates a new list). Always call get_negative_keyword_lists first to find the right shared_set_id and get_negative_keyword_list_keywords to avoid duplicating existing terms.

shared_set_id: numeric ID from get_negative_keyword_lists (shared_set.id). keywords: list of keyword strings to append (duplicates in the input list are collapsed). match_type: "EXACT", "PHRASE", or "BROAD"

Call confirm_and_apply with the returned plan_id to execute.

attach_shared_set_to_campaignsA

Attach an existing shared set to one or more campaigns — returns a PREVIEW.

Creates CampaignSharedSet linkages so the campaigns inherit the shared set's criteria (e.g. negative keywords). Most commonly used to attach a shared negative keyword list to newly-built campaigns.

Use get_negative_keyword_lists to find the shared_set_id, and get_negative_keyword_list_campaigns to inspect existing attachments.

shared_set_id: numeric ID from get_negative_keyword_lists. campaign_ids: list of numeric campaign IDs to attach the set to.

Call confirm_and_apply with the returned plan_id to execute.

detach_shared_set_from_campaignsA

Detach a shared set from one or more campaigns — returns a PREVIEW.

Removes CampaignSharedSet linkages so the campaigns no longer inherit the shared set's criteria. The shared set itself is unchanged; only the per-campaign attachment is removed.

Use get_negative_keyword_list_campaigns to inspect existing attachments before detaching.

shared_set_id: numeric ID from get_negative_keyword_lists. campaign_ids: list of numeric campaign IDs to detach the set from.

Call confirm_and_apply with the returned plan_id to execute.

draft_demographic_targetingA

Draft demographic targeting (age/gender/parental status/income) — returns a PREVIEW.

By default, Google Ads serves to all demographic segments. This tool adds criteria that EXCLUDE a segment (negative=True, default) or NARROW targeting to it (negative=False — uncommon).

Provide exactly one of ad_group_id or campaign_id. At least one of the four demographic lists must contain a value.

Accepted values:

  • age_ranges: '18-24', '25-34', '35-44', '45-54', '55-64', '65+'. Google's buckets are FIXED — 'Exclude 23-35' has no exact mapping; ask the user which buckets to use.

  • genders: 'female', 'male', 'undetermined'

  • parental_statuses: 'parent', 'not_a_parent', 'undetermined'

  • income_ranges: PERCENTILES (not currency). 'top-10', '11-20', '21-30', '31-40', '41-50', 'lower-50', 'undetermined'. Available in select countries only (US, AU, JP, etc.).

Call confirm_and_apply with the returned plan_id to execute.

update_ad_groupC

Draft an ad group update for name and/or manual CPC bid.

draft_calloutsC

Draft campaign callout assets — returns a PREVIEW.

draft_structured_snippetsA

Draft campaign structured snippet assets — returns a PREVIEW.

draft_image_assetsB

Draft campaign image assets from local PNG, JPEG, or GIF files.

pause_entityA

Draft pausing a campaign, ad group, ad, or keyword — returns a PREVIEW.

entity_type: "campaign", "ad_group", "ad", or "keyword" entity_id format by type:

  • campaign: campaign ID (e.g. "12345678")

  • ad_group: ad group ID (e.g. "12345678")

  • ad: "adGroupIdadId" (e.g. "12345678987654")

  • keyword: "adGroupIdcriterionId" (e.g. "12345678987654")

Call confirm_and_apply with the returned plan_id to execute.

enable_entityA

Draft enabling a paused campaign, ad group, ad, or keyword — returns a PREVIEW.

entity_type: "campaign", "ad_group", "ad", or "keyword" entity_id format by type:

  • campaign: campaign ID (e.g. "12345678")

  • ad_group: ad group ID (e.g. "12345678")

  • ad: "adGroupIdadId" (e.g. "12345678987654")

  • keyword: "adGroupIdcriterionId" (e.g. "12345678987654")

Call confirm_and_apply with the returned plan_id to execute.

remove_entityA

Draft REMOVING an entity — returns a PREVIEW. This is IRREVERSIBLE.

entity_type: "campaign", "ad_group", "ad", "keyword", "negative_keyword", "shared_criterion", "campaign_asset", "asset", or "customer_asset" entity_id: The resource ID. For keywords: "adGroupIdcriterionId" For negative_keywords: "campaignIdcriterionId" (use the resource_id field from get_negative_keywords) For shared_criterion: "sharedSetIdcriterionId" (use the resource_id field from get_negative_keyword_list_keywords) For campaign_asset: "campaignIdassetIdfieldType" For asset: simple asset ID For customer_asset: "assetIdfieldType"

WARNING: Removed entities cannot be re-enabled. Use pause_entity instead if you just want to temporarily disable something.

Call confirm_and_apply with the returned plan_id to execute.

draft_sitelinksA

Draft sitelink extensions for a campaign — returns a PREVIEW.

Sitelinks appear as additional links below your ad, increasing click area and directing users to specific pages.

campaign_id: the campaign to attach sitelinks to sitelinks: list of dicts, each with: - link_text (str, required, max 25 chars) — the clickable text shown - final_url (str, required) — destination URL for this sitelink - description1 (str, optional, max 35 chars) — first description line - description2 (str, optional, max 35 chars) — second description line

Google recommends at least 4 sitelinks per campaign. Fewer than 2 may not show.

Call confirm_and_apply with the returned plan_id to execute.

confirm_and_applyA

Execute a previously previewed change.

IMPORTANT: Defaults to dry_run=True. You MUST explicitly pass dry_run=false to make real changes to the ad account (Google Ads or Reddit Ads — the plan knows which platform it targets).

Config override: if 'safety.require_dry_run: true' is set in the user's config file (default ~/.adloop/config.yaml), dry_run=false is IGNORED and this tool will keep returning DRY_RUN_SUCCESS. When that happens the response includes 'dry_run_forced_by', 'config_path', and 'remediation' fields — surface those to the user verbatim and STOP retrying. Calling this tool again with dry_run=false will not change anything until the user edits the config file, sets 'require_dry_run: false', and restarts the AdLoop MCP server.

Two-phase apply: if 'safety.two_phase_apply: true' is set (always on for AdLoop Cloud tenants), dry_run=false is REFUSED with status DRY_RUN_REQUIRED until this plan_id has completed one dry_run=true pass. Run the dry run, show it to the user, then apply for real.

Reddit plans: Reddit has no validate-only mode, so the dry run re-reads the target entity and re-checks the safety caps (returned as checks); a DRY_RUN_FAILED result means the real apply would also fail.

The plan_id comes from a prior draft_* or pause/enable tool call.

list_reddit_accountsA

List Reddit businesses and ad accounts the connected Reddit user can access.

Call first to discover ad_account_id values (plus currency, time zone and approval state) for every other Reddit tool. Requires the Reddit Ads connection (adloop init → Reddit step, or Settings → Reddit Ads in Cloud).

list_reddit_funding_instrumentsA

Funding instruments (billing) and posting profiles of a Reddit ad account.

draft_reddit_campaign needs a servable funding_instrument_id; draft_reddit_ad needs the profile_id that authors the post. Both come from here.

get_reddit_campaignsA

List Reddit campaigns with status, objective, budget mode and bids.

Returns configured_status (what you set) and effective_status (what Reddit computes: PENDING_APPROVAL, REJECTED, PENDING_BILLING_INFO, ...). Money is in account currency. Budgets live on ad groups unless is_campaign_budget_optimization is true.

get_reddit_ad_groupsA

List Reddit ad groups (budget, bid, pixel, weekly schedule, targeting summary), optionally per campaign.

Reddit requires conversion_pixel_id on every ad group; insights flag ad groups without one.

get_reddit_adsA

List Reddit ads with status, rejection_reason, post and click URL.

include_copy=true adds each ad's post: type (TEXT, IMAGE, VIDEO, CAROUSEL), headline, body, destination and media, one request per distinct post, so creative can be reviewed without opening Reddit. Insights flag REJECTED ads (policy review) and ads still PENDING_APPROVAL.

get_reddit_performanceA

Reddit Ads performance: spend, impressions, clicks, CTR, CPC, conversions, CPA, ROAS.

Returns: rows per entity for the chosen level with names joined, plus totals and insights[] (zero-conversion spenders, rejected ads, empty windows).

level: "account", "campaign" (default), "ad_group" or "ad". breakdown: optional extra dimension — "date", "hour", "country", "region", "community", "keyword", "interest", "placement", "gender", "os_type". Dates are YYYY-MM-DD; default is the last 30 days in the account's time zone. 'conversions' is the account's key conversion event. Money is in account currency. Data lags up to 6 hours. compact=true: totals + top-10 rows + offender lists instead of every row.

run_reddit_reportA

Run a custom Reddit Ads report for metrics get_reddit_performance omits.

fields: Reddit report field names, e.g. ["SPEND", "CLICKS", "REACH", "VIDEO_STARTED", "CONVERSION_PURCHASE_TOTAL_VALUE", "KEY_CONVERSION_TOTAL_COUNT"]. breakdowns: up to 3 of DATE, HOUR, CAMPAIGN_ID, AD_GROUP_ID, AD_ID, COUNTRY, REGION, COMMUNITY, KEYWORD, INTEREST, PLACEMENT, GENDER, OS_TYPE (HOUR and DATE cannot be combined). Dates are account-local days. filter: Reddit filter expression (e.g. "campaign_id==abc123"). Microcurrency fields are converted to currency amounts.

get_reddit_pixelsA

Reddit pixels of the account and when each event (purchase, sign_up, lead, ...) last fired.

Run before conversion-optimized campaigns: insights flag pixels that never fired and ad groups optimizing for an event their pixel has never sent. Every new ad group needs a conversion_pixel_id from here.

search_reddit_targetingA

Look up targeting options for draft_reddit_ad_group.

kind: "communities" (subreddits; query required), "interests" (query filters by name), "geolocations" (country ISO code and/or city query), "languages" (upper-case ISO 639-1 codes), "keywords" (comma-separated seed terms → suggestions with Reddit-wide monthly views), or "community_suggestions" (Reddit's related-community picks for seed communities in query, e.g. "PPC,googleads", and/or a website_url). Returns ids/names to pass into the targeting lists.

get_reddit_account_historyA

Who changed what in the Reddit ad account: field, before/after, member, time.

The first thing to check when performance moves. Default window is the last 30 days (account-local days). Optionally filter to one entity_type ("campaign", "ad_group", "ad") with entity_ids; child entities are included. Changes made through AdLoop show under the connected Reddit user, like changes made in Ads Manager. Money fields are in account currency.

estimate_reddit_ad_groupA

Audience size, delivery estimate and Reddit's suggested bid for a planned ad group — read-only.

Reddit's counterpart of estimate_budget: run it before draft_reddit_ad_group with the same targeting and budget. Returns the reachable and targetable audience (fixed 30-day basis), estimated impressions/clicks/reach for the schedule (default: tomorrow for 30 days), and the minimum/suggested bid range for bid_type in account currency. Nothing is created.

pause_reddit_entityA

Draft pausing a Reddit campaign, ad group or ad — returns a PREVIEW.

entity_type: "campaign", "ad_group" or "ad"; entity_id from the read tools. Call confirm_and_apply with the returned plan_id to execute.

enable_reddit_entityA

Draft enabling (configured_status=ACTIVE) a Reddit campaign, ad group or ad — PREVIEW.

Enabling a new ad sends it to Reddit policy review (PENDING_APPROVAL). Call confirm_and_apply with the returned plan_id to execute.

remove_reddit_entityA

Draft ARCHIVING a Reddit campaign, ad group or ad — irreversible, PREVIEW.

Reddit has no hard delete for entities that ran; ARCHIVED is permanent. Prefer pause_reddit_entity unless the user explicitly wants it gone. Requires double confirmation. Call confirm_and_apply to execute.

update_reddit_campaignA

Draft changes to a Reddit campaign — name, run dates, spend cap, and (CBO only) budget/bid/schedule.

daily_budget / lifetime_budget / bid_* apply only when the campaign uses campaign budget optimization; otherwise the budget lives on its ad groups (use update_reddit_ad_group). Budgets are in account currency and checked against max_daily_budget. The preview shows old → new per field. schedule: weekly delivery windows ("time of day" in Ads Manager), a list of blocks like {"days": "MON-FRI", "start_hour": 13, "end_hour": 23} (day names, hours 0-23, end_hour inclusive) or the native {"start_day": "FRI", "start_hour": 22, "end_day": "SAT", "end_hour": 3}. [] clears it (deliver at any time). Hours apply in each viewer's local time, not the account time zone.

update_reddit_ad_groupA

Draft changes to a Reddit ad group — budget, bid, run dates, weekly schedule, targeting.

Budget (daily_budget or lifetime_budget + end_time) is checked against max_daily_budget; bid_value against max_bid_increase_pct. Targeting lists REPLACE the current value of each key you pass (pass the full list); keys you omit are preserved. Ids/names come from search_reddit_targeting. schedule: weekly delivery windows ("time of day" in Ads Manager), a list of blocks like {"days": "MON-FRI", "start_hour": 13, "end_hour": 23} (day names, hours 0-23, end_hour inclusive) or the native {"start_day": "FRI", "start_hour": 22, "end_day": "SAT", "end_hour": 3}. [] clears it (deliver at any time). Hours apply in each viewer's local time, not the account time zone. locations: placements, FEED and/or COMMENTS_PAGE (conversation pages).

update_reddit_adA

Draft changes to a Reddit ad — name, landing URL (click_url), comments on/off.

click_url is verified to be reachable. The headline and body of a live Reddit post cannot be edited; for new copy use draft_reddit_ad and pause the old ad. Preview shows old → new; confirm_and_apply executes.

draft_reddit_campaignA

Draft a new Reddit campaign (created PAUSED) — returns a PREVIEW.

objective: CLICKS, CONVERSIONS, IMPRESSIONS, LEAD_GENERATION, APP_INSTALLS, CATALOG_SALES or VIDEO_VIEWABLE_IMPRESSIONS. funding_instrument_id from list_reddit_funding_instruments. By default the budget lives on the ad groups; set campaign_budget_optimization=true to hold it on the campaign (then daily_budget or lifetime_budget, bid_strategy, bid_type and conversion_pixel_id are required). Budgets are checked against max_daily_budget. Times are ISO 8601. Follow up with draft_reddit_ad_group. schedule: weekly delivery windows ("time of day" in Ads Manager), a list of blocks like {"days": "MON-FRI", "start_hour": 13, "end_hour": 23} (day names, hours 0-23, end_hour inclusive) or the native {"start_day": "FRI", "start_hour": 22, "end_day": "SAT", "end_hour": 3}. [] clears it (deliver at any time). Hours apply in each viewer's local time, not the account time zone.

draft_reddit_ad_groupA

Draft a new Reddit ad group (created PAUSED) — returns a PREVIEW.

Required: campaign_id, ad_group_name, conversion_pixel_id (Reddit rule; from get_reddit_pixels) and at least one targeting list (geolocations, communities, interests or keywords — ids/names from search_reddit_targeting). For non-CBO campaigns also daily_budget (or lifetime_budget + end_time), bid_strategy (BIDLESS, MANUAL_BIDDING, MAXIMIZE_VOLUME, TARGET_CPX) and bid_type (CPC, CPM, CPV, CPV6, CPV15); MANUAL_BIDDING/TARGET_CPX need bid_value. optimization_goal is the pixel event to optimize for (PURCHASE, SIGN_UP, LEAD, PAGE_VISIT, ...). Budgets are checked against max_daily_budget.

schedule: weekly delivery windows ("time of day" in Ads Manager), a list of blocks like {"days": "MON-FRI", "start_hour": 13, "end_hour": 23} (day names, hours 0-23, end_hour inclusive) or the native {"start_day": "FRI", "start_hour": 22, "end_day": "SAT", "end_hour": 3}. Omit for delivery at any time. Hours apply in each viewer's local time, not the account time zone. locations: placements, FEED and/or COMMENTS_PAGE (conversation pages).

draft_reddit_adA

Draft a Reddit ad: creates a post on the profile, then the ad (PAUSED) — PREVIEW.

profile_id from list_reddit_funding_instruments (profiles). post_type TEXT (headline + optional body) or IMAGE (headline + public image_url). click_url is verified to be reachable before drafting — never point ads at unverified pages. call_to_action is one of Reddit's fixed labels (Learn More, Sign Up, Shop Now, Download, ...). Comments are public on Reddit ads; pass allow_comments=false to disable them.

post_id (t3_...) promotes an EXISTING post instead, keeping its upvotes and comments: no post is created, headline/body/image are ignored, and profile_id defaults to the post's. TEXT posts take no click_url (they open themselves); media posts default click_url to the post's destination.

draft_key_eventA

Draft marking a GA4 event as a key event (conversion) — returns a PREVIEW.

The fix for "the event fires but isn't tracked as a conversion": attribution_check / validate_tracking diagnose it, this closes the loop. counting_method: ONCE_PER_EVENT (purchases) or ONCE_PER_SESSION (sign-ups). Call confirm_and_apply with the returned plan_id to execute. Applies to future data only.

draft_create_conversion_actionA

Draft a new Google Ads ConversionAction — returns a PREVIEW.

type_: AD_CALL, WEBSITE_CALL, WEBPAGE, WEBPAGE_CODELESS, GOOGLE_ANALYTICS_4_CUSTOM, etc. category: PHONE_CALL_LEAD, SUBMIT_LEAD_FORM, PURCHASE, ... (default DEFAULT).

A positive default_value with always_use_default_value=False is a legal "fallback" config — the preview warns but does NOT flip the flag. include_in_conversions_metric is IMMUTABLE on create (change it later via draft_update_conversion_action). Call confirm_and_apply with the returned plan_id to execute.

draft_update_conversion_actionA

Draft a partial UPDATE of an existing ConversionAction — returns PREVIEW.

Only the parameters you pass non-empty/non-default are sent to the API. Use to rename, demote a Primary to Secondary, change value, adjust the call-duration threshold, or change attribution. Find the ID via: SELECT conversion_action.id, conversion_action.name FROM conversion_action. Call confirm_and_apply with the returned plan_id to execute.

draft_remove_conversion_actionA

Draft a REMOVAL of a ConversionAction — returns a PREVIEW.

Removal stops counting and drops the action from goal lists; historical data is preserved but the action is irreversible. SMART_CAMPAIGN_* and GOOGLE_HOSTED types reject removal with MUTATE_NOT_ALLOWED. Call confirm_and_apply with the returned plan_id to execute.

validate_trackingA

Compare tracking events found in the codebase against actual GA4 data.

First, search the user's codebase for gtag('event', ...) or dataLayer.push calls and extract event names. Then pass those names here to check which ones actually fire in GA4.

Returns: matched events, events missing from GA4, unexpected GA4 events, and auto-collected events (page_view, session_start, etc.).

generate_tracking_codeA

Generate a GA4 event tracking JavaScript snippet.

Produces ready-to-paste gtag code for the specified event. Includes recommended parameters for well-known GA4 events (sign_up, purchase, etc.). Optionally checks GA4 to warn if the event already fires.

trigger: "form_submit", "button_click", or "page_load" — wraps the gtag call in an appropriate event listener. Empty = bare gtag call.

estimate_budgetA

Forecast clicks, cost, and conversions for a set of keywords.

Uses Google Ads Keyword Planner to estimate campaign performance without creating anything. Essential for budget planning before launching campaigns. Per-keyword max_cpc values are collapsed to a campaign-level manual CPC cap (the highest one) — the Ads API forecast takes no per-keyword bids.

keywords: list of {"text": "keyword", "match_type": "EXACT|PHRASE|BROAD", "max_cpc": 1.50} max_cpc is optional (defaults to 1.00 in account currency) geo_target_id: geo target constant (2276=Germany, 2840=USA, 2826=UK, 2250=France) language_id: language constant (1000=English, 1001=German, 1002=French, 1003=Spanish) daily_budget: if provided, insights will show what % of traffic the budget captures forecast_days: forecast horizon in days (default 30)

discover_keywordsA

Discover new keyword ideas using Google Ads Keyword Planner.

Mirrors the "Discover new keywords" UI in Keyword Planner:

Set include_monthly_volumes=true for per-month search history (last 24 months, top-20 ideas) plus a seasonality insight — use when the user asks about demand trends, seasonality, or "when should I ramp budget".

  • Both together: keywords + url for more targeted ideas

Returns keyword ideas sorted by avg monthly search volume, with competition level (LOW/MEDIUM/HIGH) and top-of-page bid range.

geo_target_id: geo target constant (2276=Germany, 2840=USA, 2826=UK) language_id: language constant (1000=English, 1001=German, 1002=French) page_size: max keyword ideas to return (default 50, max 1000)

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

B3.2/5.0

Scored across 91 tools

Disambiguation3/5

Most tools are grouped into clear platform/resource families (draft_*, get_*_performance, list_gtm_*, get_reddit_*), and individual purposes are usually distinct. However, the negative-keyword/shared-set cluster (add_negative_keywords, propose_negative_keyword_list, add_to_negative_keyword_list, attach_shared_set_to_campaigns) and the parallel generic vs. Reddit-specific entity verbs (pause_entity vs. pause_reddit_entity) create real selection ambiguity.

Naming Consistency2/5

The naming mixes several conventions: draft_* for previews of both creates and updates/removals, generic entity verbs (pause_entity, remove_entity, enable_entity), platform-prefixed verbs (pause_reddit_entity), and ad hoc verbs (propose_negative_keyword_list, audit_event_coverage, confirm_and_apply). The draft_ prefix also changes meaning across tools, making patterns unpredictable.

Tool Count1/5

91 tools spanning Google Ads, GA4, GTM, Reddit, Search Console, Merchant Center, and PageSpeed is an extreme surface area. Even for a genuinely multi-platform server, this volume imposes a severe agent selection burden and exceeds what the tool-set coherence scale treats as manageable.

Completeness4/5

For Google Ads and Reddit the lifecycle is well covered: create/draft, update, pause, enable, remove, report, and conversion actions are all represented, with deep reporting and auditing coverage across GA4 and GTM. Notable gaps remain, primarily that GTM tools are read-only (no tag/trigger creation/editing) and some Ads reads (conversion_actions, ad groups) are only reachable via run_gaql.

Maintenance

ActivityActive
ResponsivenessWithin a week