New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

adloop

Package Overview
Dependencies
Maintainers
1
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

adloop

The AI command center for Google Ads, Reddit Ads, GA4, and tracking code.

pipPyPI
Version
0.22.1
Weekly downloads
516
23.44%
Maintainers
1
Weekly downloads
 
Created

AdLoop

The AI command center for Google Ads, Reddit Ads, GA4, and tracking code.

PyPI License: MIT Python 3.11+ MCP Compatible Google Ads API GA4 Data API GitHub stars

An MCP server that gives your AI assistant read + write access to Google Ads, Reddit Ads and GA4 — with safety guardrails that prevent accidental spend.

☁️ Skip the setup — use AdLoop Cloud (free plan, no card)  ·  or self-host: pip install adloop

[!TIP] AdLoop Cloud is the hosted version of this project, with a free plan that needs no credit card. Connect Google in two clicks and use the full toolset from claude.ai, ChatGPT, Claude Code, Cursor, or Gemini. No Google Cloud project, no API access application, no OAuth verification wait. EU-hosted, GDPR-first, DPA included.

📚 Documentation: docs.getadloop.com — setup guides per AI client, toolsets, the safety model, and troubleshooting for both editions.

Cloud or Self-Hosted?

Both versions run the same tools with the same safety model. The difference is who handles the plumbing:

☁️ AdLoop Cloud🛠️ Self-hosted (this repo)
SetupConnect Google in two clicks~5 min: own Google Cloud project + adloop init
Google Cloud projectNot neededRequired (free)
Ads API accessNot neededGranted to your Cloud project (no developer token, no MCC since Sept 2026)
Works withclaude.ai, ChatGPT, Claude Code, Cursor, GeminiClaude Code, Cursor, Claude Desktop, any local MCP client
Where your data flowsEU servers (Germany), GDPR-first, DPA included100% your machine — nothing leaves it
UpdatesAutomaticpip install -U adloop
PriceFree plan, no card; paid plans for more accounts and volumeFree forever (MIT)

Not sure? Start with Cloud — it's the fastest way to see what AdLoop can do, and it's the only way to use AdLoop from claude.ai or ChatGPT. Self-host when you want everything on your own machine or need to modify the code. And if you're here to hack on AdLoop itself: welcome, keep scrolling.

What It Solves

AdLoop exists because managing Google Ads alongside your code is a mess. These are the specific problems it handles:

  • "My conversions dropped and I don't know why." AdLoop cross-references Ads clicks, GA4 sessions, and conversion events in one query. It detects whether the gap is from GDPR consent rejection, broken tracking, or an actual landing page problem — before you waste hours checking each dashboard separately.

  • "I'm wasting ad spend on irrelevant searches." Pull your search terms report, identify the junk, and add negative keywords — all from a single conversation in your IDE. No context-switching to the Ads UI.

  • "Is my tracking even working?" Compare the event names in your actual codebase against what GA4 is receiving. Find the mismatches: events you fire that GA4 never sees, events GA4 records that you didn't know about.

  • "I need to create ads but the Google Ads UI is hostile." Draft responsive search ads, create campaigns, add keywords — all through natural language. Every change shows a preview first. Nothing goes live without your explicit confirmation. New ads and campaigns start paused.

  • "My landing page gets paid traffic but nobody converts." AdLoop joins your ad final URLs with GA4 page-level data. See which pages get clicks but no conversions, which have high bounce rates, and which ones are orphaned from any ad campaign.

  • "Are conversions even being tagged on every page?" AdLoop reads your live Google Tag Manager container, joins it against the events in your codebase and the events firing in GA4, and tells you exactly which conversions are being captured, which tags are paused, which page-scope filters are too narrow, and which codebase events have no tag at all — the kind of three-way audit GTM Preview can't give you in a single view.

  • "I don't know if my EU consent setup is causing data gaps." In Europe, 30-70% of users reject analytics cookies. AdLoop accounts for this automatically — it won't diagnose a normal GDPR consent gap as broken tracking.

Built From Real Usage

Every tool exists because of an actual problem hit while running real Google Ads campaigns. The cross-reference tools exist because we kept manually asking the AI to "get Ads data, then get GA4 data, then compare them" — so we automated the join. The Broad Match + Manual CPC safety rule exists because the AI once created that exact combination and wasted budget. The GDPR consent awareness exists because the AI kept diagnosing normal EU cookie rejection as broken tracking.

The best features come from real workflows. If you're using AdLoop and find yourself wishing it could do something it can't, open an issue describing your situation — not just "add feature X" but "I was trying to do Y and couldn't because Z." The context matters more than the request.

All Tools

Quick start: pip install adloop or git clone https://github.com/kLOsk/adloop.git && cd adloop && uv sync && uv run adloop init — or zero setup on AdLoop Cloud

Diagnostics

ToolWhat It Does
health_checkTest OAuth, GA4, and Ads connectivity in one call — actionable error messages if anything is broken. Also reports the pinned Google Ads API version and warns if a newer version is available.

GA4 Read Tools

ToolWhat It Does
get_account_summariesList GA4 accounts and properties
run_ga4_reportCustom reports: sessions, users, key events, page performance. Optional dimension and metric filters, ordering, paging, and a comparison period
run_realtime_reportLive data — verify tracking fires after deploys
get_tracking_eventsAll configured events and their volume
list_key_eventsThe property's key events (conversions) with counting method, create time and whether they can be deleted
list_ga4_dimensions_and_metricsEvery dimension and metric the property can report on, including custom ones, with search

Google Ads Read Tools

ToolWhat It Does
list_accountsDiscover accessible Ads accounts
get_campaign_performanceCampaign metrics: impressions, clicks, cost, conversions, CPA, Search impression share and share lost to budget/rank
get_ad_performanceAd copy analysis: headlines, descriptions, CTR, policy approval status and topics
get_keyword_performanceKeywords — quality scores, competitive metrics
get_search_termsWhat users actually searched before clicking
get_negative_keywordsList direct campaign-level negative keywords
get_negative_keyword_listsList all shared negative keyword lists (SharedSets) — names, IDs, status, keyword count
get_negative_keyword_list_keywordsList the keywords inside a specific shared negative keyword list
get_negative_keyword_list_campaignsList which campaigns a shared negative keyword list is attached to
get_recommendationsGoogle's auto-generated recommendations with type, estimated impact, and campaign context
get_pmax_performancePerformance Max campaign metrics with network breakdown + asset group ad strength
get_pmax_assetsPer-asset details for PMax — field type, serving status, content
get_detailed_asset_performanceTop-performing asset combinations — which headline+description+image combos Google selects most
get_audience_performanceAudience segment performance — remarketing, in-market, affinity, demographics
get_change_historyWhat changed before the drop: who changed what and when, through which client (UI, API, scripts, auto-applied recommendations), last 30 days
get_demographic_targetingList demographic criteria (age/gender/parental status/income) on an ad group or campaign
suggest_brandsResolve a brand name to the brands Google recognizes — brand ID, name, state, URLs
check_brand_namesCheck a shortlist of brand names against Google's brand knowledge graph (max 25 per call)
get_brand_listsList brand lists (SharedSets of type BRANDS) — ID, name, status, member count
get_brand_list_brandsList the brands inside a list, with the Commercial KG MID and the criterion ID for removals
get_brand_list_campaignsWhich campaigns a brand list is attached to, and whether each attachment excludes or targets
get_ai_max_settingsAI Max knobs per campaign and ad group — enable_ai_max, bundling_required, the full asset_automation_settings list and each ad group's disable_search_term_matching
get_custom_conversion_goalsCustom conversion goals with their actions, plus one campaign's goal config
run_gaqlArbitrary GAQL queries for anything else

Brand lists — a list is a SharedSet of type BRANDS; attaching it to a campaign is a CampaignCriterion.brand_list (negative=true excludes, false restricts), not a CampaignSharedSet like negative keyword lists. remove_from_brand_list and detach_brand_list_from_campaigns remove for real — SharedCriterion has no status field.

Brand targeting — brand criteria (brand lists, brand exclusions) target a brand's Commercial Knowledge Graph ID, not its display name. Use suggest_brands for a single name or check_brand_names for a shortlist to get the ID; exact_match marks a candidate whose name matches apart from case and punctuation, everything else is a Google suggestion.

AI Max is the container for Search brand exclusions — Google rejects a brand list on a plain Search campaign with "For search advertising channel, brand lists can only be applied to exclusive targeting, broad match campaigns for inclusive targeting or PMax generated campaigns." draft_prepare_brand_exclusions sets the state that makes the exclusion usable without handing Google the automations: AI Max on, search term matching off per ad group, text and final-URL automation opted out. draft_ai_max_settings is the general form when only parts of that state should change — it refuses a plan that would leave an automation on unnoticed, and asks for a second confirmation when that automation was requested explicitly.

Custom conversion goals — a named set of conversion actions that a campaign can be pointed at. get_custom_conversion_goals shows the goals with their actions and a campaign's current goal config; draft_custom_conversion_goal creates a set, draft_update_custom_conversion_goal renames or replaces its actions, draft_assign_custom_conversion_goal points a campaign at it and draft_clear_custom_conversion_goal puts the campaign back on the account-level goals. Only the goal and the campaign's goal config are touched — conversion actions, bidding and budgets stay as they are.

Compact mode — get_campaign_performance, get_keyword_performance, get_search_terms, and get_ad_performance accept compact=true: account totals, breakdowns, top-10 rows, and pre-computed offender lists (zero-conversion spenders, low-QS keywords, negative-keyword candidates, thin RSAs) instead of every row. ~90% smaller responses — built for account audits so raw tables don't flood your AI's context.

Cross-Reference Tools (GA4 + Ads Combined)

These tools call both APIs internally and return unified results with auto-generated insights. They're the core of what makes AdLoop different from having separate GA4 and Ads tools.

ToolWhat It Does
analyze_campaign_conversionsMaps Ads clicks → GA4 sessions → conversions per campaign. Detects GDPR consent gaps, computes real CPA, compares paid vs organic channels.
landing_page_analysisJoins ad final URLs with GA4 page data. Shows conversion rate, bounce rate, and engagement per landing page. Flags pages with paid traffic but zero conversions.
attribution_checkCompares Ads-reported conversions vs GA4 events. Diagnoses whether discrepancies are from GDPR consent, attribution windows, or broken tracking.

Tracking Tools

ToolWhat It Does
validate_trackingCompare event names found in your codebase against what GA4 actually records. Returns matched, missing, and unexpected events with diagnostics.
generate_tracking_codeGenerate ready-to-paste GA4 gtag JavaScript for any event, with recommended parameters for well-known events (sign_up, purchase, etc.) and optional trigger wrappers.

Google Tag Manager Tools

These tools read the live GTM container and join it with the codebase + GA4 to find tracking gaps that pure GA4 inspection can't catch — page-scoped triggers, paused tags, dynamic event names, brittle CSS selectors, and codebase events with no tag wired up at all.

ToolWhat It Does
audit_event_coverageThe flagship. Three-way join: codebase events ↔ GTM tags ↔ GA4 actual fires. For each event name in expected_events, returns one of 10 statuses (ok, no_tag_no_fire, tag_paused, tag_active_but_not_firing, gtm_only_firing, ga4_only, etc.) plus auto-generated insights for the gaps.
list_gtm_accountsDiscover accessible GTM accounts
list_gtm_containersList containers under an account — returns numeric container_id (needed by other tools), public GTM-XXXXXXX ID, and usage context (web/iOS/Android/server)
list_gtm_tagsEvery tag in the live container with parsed event names and resolved firing/blocking trigger names. Pass workspace_id to read a workspace's unpublished state instead, so a just-drafted tag is visible (also on get_gtm_tag, list_gtm_triggers, get_gtm_trigger, list_gtm_variables)
get_gtm_tagFull raw config for a single tag — every parameter, firing/blocking triggers with filter conditions, priority, pause status, sampling
list_gtm_triggersEvery trigger with filter conditions parsed to readable text (e.g. {{Page Path}} contains service-promotions, {{Form ID}} NOT contains wf-form-...). Renders the negate flag explicitly.
get_gtm_triggerFull trigger config + reverse lookup of every tag that uses it. Includes parsed element_visibility block (selector, on-screen ratio, firing frequency) for elementVisibility triggers and group_member_trigger_ids for triggerGroup types
list_gtm_variablesCustom variables (data layer, constants, JS) plus enabled built-in variables
list_gtm_workspacesList drafts (workspaces) under a container — workspace IDs are needed by get_gtm_workspace_diff
get_gtm_workspace_diffDrafted-but-not-published changes — common cause of "I edited a tag but nothing happened". Returns is_clean: true when nothing is pending.
list_gtm_versionsVersion history, newest first, with version IDs and entity counts. The Tag Manager API returns no timestamps or author for versions.
get_gtm_versionName, notes and tag/trigger names for a single historical container version
get_gtm_version_diffWhat a publish changed: added, removed and changed tags, triggers and variables with the changed fields, plus built-in variables enabled or disabled. Defaults to the live version against the one before it; two or three API calls.

GTM write tools (opt-in)

Off by default. Set gtm.write_enabled: true in ~/.adloop/config.yaml and restart; the next call asks you to re-consent with the Tag Manager edit + publish scopes, which are never part of the default grant. Everything follows the usual draft → preview → confirm_and_apply flow, and tag/trigger edits only touch a workspace until you publish it.

ToolWhat It Does
draft_gtm_tagCreate a tag, or update one by tag_id. Updates are a read-modify-write: parameters merge by key and every field you don't pass (priority, consent settings, firing options, schedule, folder, …) is preserved. Tag types are canonical GTM template IDs (googtag, gaawe, awct, sp, gclidw, …) or cvt_<id> gallery templates.
draft_gtm_triggerCreate a trigger, or update one by trigger_id (type is immutable). custom_event_name wires a customEvent trigger to a dataLayer event.
draft_delete_gtm_entityDelete a workspace tag or trigger. Triggers still referenced by a tag are refused up front, with the tags listed.
draft_publish_gtm_workspacePublish a workspace live. The preview lists every pending change, including edits other people made in the GTM UI.
draft_rollback_gtm_versionRepublish an older container version live. The preview is the diff from the live version to the target; the plan pins the live version, so apply refuses if someone publishes in between. Workspaces are left as they are.

Safety gates specific to GTM:

  • Custom HTML runs arbitrary JavaScript on your site, so creating or editing an html tag — or publishing a workspace that adds or changes one — is refused unless gtm.allow_custom_html: true is also set. Pausing or deleting one is always allowed.
  • No stale writes. Updates and deletes pin the entity's fingerprint from the preview, and publish pins the workspace's pending changes; if someone edits the container in between, apply refuses and you re-draft.
  • Publishing stops on merge conflicts or GTM compiler errors. The dry run runs a Tag Manager quick preview first (Tag Manager has no validate-only mode), so compiler errors surface before a version is even created. That preview is a POST and stores a preview version in the container — nothing is published and no live tag changes; the rest of the dry run only reads.
  • The publish and rollback results name the version they replaced (previous_live_version_id), so rolling back is one step: draft_rollback_gtm_version with that id. If the version was created but publishing it failed, the error carries both the created version id and the still-live one.
  • Per-operation names for safety.blocked_operations: gtm_create_tag, gtm_update_tag, gtm_delete_tag, gtm_create_trigger, gtm_update_trigger, gtm_delete_trigger, gtm_publish_workspace, gtm_rollback_version.

The Google account also needs Edit permission on the container (and Publish to publish) under Admin → User Management.

Setup for GTM tools — Enable the Tag Manager API v2 in your GCP project, then add your AdLoop credentials' email (the OAuth user, or the service account email if using a service account) as a Read user on the GTM container under Admin → User Management. Service accounts pick up access on the next call. OAuth users upgrading from an earlier AdLoop version must re-authorize once: the GTM scope is new, so delete ~/.adloop/token.json and run any tool to re-consent — until then GTM tools return a permissions error.

Search Console Tools

ToolWhat It Does
list_gsc_sitesList Search Console properties the connected account can access
run_gsc_reportOrganic search analytics: clicks, impressions, CTR, position by query/page/country/device/date/searchAppearance/hour. Supports fresh data (data_state="all"), aggregation_type, and paging past 25,000 rows with start_row

Web Performance Tools

ToolWhat It Does
analyze_page_speedPageSpeed Insights for landing pages: Lighthouse score, Core Web Vitals, real-user CrUX data (falls back to origin-wide data, labelled as such, when the page has too little traffic), top fixes. No OAuth needed (optional API key).

Merchant Center Tools

ToolWhat It Does
list_merchant_accountsDiscover accessible Merchant Center accounts
get_merchant_feed_healthFeed health — approved/pending/disapproved counts per reporting context, top product issues with docs, account-level issues. Disapprovals silently starve Shopping/PMax.

Setup for Merchant Center tools — Enable the Merchant API in your GCP project (the Content API for Shopping is deprecated). The Merchant API has no read-only scope; AdLoop uses it strictly read-only. Upgrading OAuth users re-authorize once.

Setup for GSC tools — Enable the Search Console API in your GCP project. Upgrading OAuth users must re-authorize once for the new scope (delete ~/.adloop/token.json, run any tool). The killer combo: cross-reference organic queries with get_keyword_performance to find paid/organic cannibalization and untapped keyword opportunities.

Reddit Ads Tools

A second ad platform, same safety model. Reddit is a separate connection: its own developer app, its own OAuth, no developer token and no approval process. Every tool takes ad_account_id (defaults to reddit.ad_account_id).

ToolWhat It Does
list_reddit_accountsDiscover businesses and ad accounts (currency, time zone, approval state)
list_reddit_funding_instrumentsBilling instruments and posting profiles — prerequisites for creating campaigns and ads
get_reddit_campaigns / get_reddit_ad_groups / get_reddit_adsStructure with configured vs effective status, budgets, bids, pixel, targeting, weekly schedule, rejection reasons; include_copy returns each ad's post (headline, body, destination, media)
get_reddit_performanceSpend, clicks, CTR, CPC, conversions, CPA, ROAS per account/campaign/ad group/ad, optional breakdown (date, country, community, keyword, placement, …), compact mode with insights
run_reddit_reportRaw reports endpoint for any metric (REACH, video, per-event conversions)
get_reddit_pixelsPixels and when each event last fired — flags ad groups optimizing for events the pixel never sent
search_reddit_targetingCommunities, interests, geolocations, languages, keyword suggestions, and Reddit's related-community suggestions (by seed communities or website)
get_reddit_account_historyWho changed what and when: field, before/after, member
estimate_reddit_ad_groupAudience size, delivery estimate and suggested bid range for a planned ad group (Reddit's counterpart of estimate_budget)
pause_reddit_entity / enable_reddit_entity / remove_reddit_entityStatus changes through the preview gate (remove = ARCHIVE, irreversible, double-confirmed)
update_reddit_campaign / update_reddit_ad_group / update_reddit_adBudget, bid, run dates, weekly schedule (day names, viewer-local hours), targeting and placements (validated with Reddit at draft time), landing URL and comment changes with old → new previews, budget cap and bid-increase guards
draft_reddit_campaign / draft_reddit_ad_group / draft_reddit_adCreate campaign → ad group (pixel + targeting required) → post + ad, or promote an existing post with post_id. Everything is created PAUSED.

Setup for Reddit Ads tools — In Reddit Ads Manager open Business Manager → Developer Application → Create app (business admins only; no approval wait). Register the redirect URL exactly as http://localhost:8765/callback, then run adloop init and complete the Reddit Ads step: it opens Reddit's consent page (scopes adsread + adsedit, permanent grant), stores the refresh token at ~/.adloop/reddit_token.json, and lets you pick the default ad account. Reddit rate-limits per user (reporting: 60 requests/min) and requires a descriptive User-Agent, which AdLoop builds from your app id and Reddit username. Reddit has no validate-only mode, so confirm_and_apply(dry_run=true) re-reads the target and re-checks the safety caps instead.

Planning Tools

ToolWhat It Does
discover_keywordsDiscover new keyword ideas from seed keywords and/or a URL — with optional per-month search history + seasonality insights (include_monthly_volumes) using Google Ads Keyword Planner. Returns avg monthly searches, competition level, and top-of-page bid range.
estimate_budgetForecast clicks, cost, and conversions for a set of keywords using Google Ads Keyword Planner. Supports geo/language targeting. Essential for budget planning before launching campaigns.

Google Ads Write Tools

All write operations follow a draft → preview → confirm workflow. Nothing executes without explicit approval.

ToolWhat It Does
draft_campaignCreate a full campaign structure — budget + campaign (PAUSED) + ad group + optional keywords. Supports Search partners, display expansion, and max_cpc for either MANUAL_CPC initial ad-group bids or TARGET_SPEND (Maximize Clicks) CPC caps.
update_campaignModify existing campaign settings — bidding, budget, geo/language targeting, Search partners, display expansion, and TARGET_SPEND (Maximize Clicks) max_cpc caps.
draft_ad_groupCreate a paused SEARCH_STANDARD ad group inside an existing campaign, with optional MANUAL_CPC max_cpc.
update_ad_groupUpdate an ad group name and/or MANUAL_CPC max_cpc. Use pause_entity / enable_entity for ad-group status changes.
draft_responsive_search_adCreate RSA preview (3-15 headlines ≤30 chars, 2-4 descriptions ≤90 chars). Warns if headline/description count is below best practice.
draft_calloutsCreate campaign callout assets from 1-25 character text snippets.
draft_structured_snippetsCreate campaign structured snippet assets using official header values and 3-10 snippet values.
draft_image_assetsCreate campaign image assets from local files or public image URLs (PNG, JPEG, or GIF).
draft_keywordsPropose keyword additions with match types. Proactively checks bidding strategy — flags BROAD match on campaigns without Smart Bidding as dangerous in the preview.
add_negative_keywordsPropose negative keywords directly on a campaign
add_negative_locationsPropose negative geo exclusions on a campaign — exclude cities/regions while keeping broader positive targets
draft_key_eventMark a GA4 event as a key event (conversion) — the fix for "fires but isn't tracked as a conversion"
draft_delete_key_eventRemove a GA4 key event. The event keeps firing but no longer counts as a conversion, for future data only
draft_demographic_targetingPropose demographic criteria (age, gender, parental status, income) — exclusions by default
propose_negative_keyword_listDraft a shared negative keyword list (SharedSet) and attach it to a campaign — reusable across multiple campaigns
propose_brand_listDraft a brand list (SharedSet of type BRANDS) from Commercial KG MIDs and optionally attach it to campaigns — negative=true (default) excludes the brands, false restricts targeting to them
add_to_brand_listDraft adding brands to an existing brand list
remove_from_brand_listDraft removing brands from a list (SharedCriteria have no status — removal is the only way; asks for a second confirmation)
attach_brand_list_to_campaignsDraft attaching an existing brand list to campaigns as CampaignCriterion.brand_list
detach_brand_list_from_campaignsDraft detaching a brand list from campaigns (removes only the criterion, the list stays)
draft_ai_max_settingsDraft AI Max controls for a Search campaign: enable_ai_max, per-ad-group disable_search_term_matching, plus text and final-URL asset automation (each OPTED_IN / OPTED_OUT / UNCHANGED)
draft_prepare_brand_exclusionsDraft the safe standard state in one step: AI Max on, search term matching off for every non-removed ad group, text and final-URL automation opted out. Touches nothing else
draft_custom_conversion_goalCreate a custom conversion goal (a named set of conversion actions)
draft_update_custom_conversion_goalRename a custom conversion goal and/or replace its action list (list replace, not append)
draft_assign_custom_conversion_goalPoint a campaign at a custom conversion goal (goal_config_level = CAMPAIGN)
draft_clear_custom_conversion_goalPut a campaign back on the account-level goals (rollback)
pause_entityPause a campaign, ad group, ad, or keyword
enable_entityRe-enable a paused entity
remove_entityPermanently remove an entity (irreversible — prefers pause). Supports keywords, negative keywords, ads, ad groups, campaigns.
confirm_and_applyExecute a previously previewed change

Orchestration Rules

AdLoop ships with orchestration rules that teach the AI how to combine these tools — marketing workflows, GAQL syntax, safety protocols, GDPR awareness, and best practices. Without rules, the AI has tools but doesn't know the playbook.

  • Cursor: .cursor/rules/adloop.mdc (canonical source)
  • Claude Code: .claude/rules/adloop.md (synced from Cursor rules via scripts/sync-rules.py)

The rules include:

  • Orchestration patterns for common workflows (performance review, conversion diagnosis, campaign creation, negative keyword hygiene, keyword discovery, tracking validation, budget planning, landing page analysis)
  • GAQL quick reference with syntax, common queries, and gotchas
  • Safety rules including Broad Match + Manual CPC prevention and pre-write validation
  • Ad copy character limit guidance (30-char headlines are shorter than you think)
  • GDPR consent awareness to prevent false tracking diagnoses in EU markets

Slash Commands (Claude Code)

AdLoop includes pre-built slash commands in .claude/commands/ for common workflows:

CommandWhat It Does
/analyze-performanceFull performance review across Google Ads + GA4
/create-adCreate a responsive search ad with safety checks
/diagnose-trackingDiagnose tracking and conversion issues
/optimize-campaignFull optimization checklist for a campaign
/create-campaignCreate a new search campaign with budget estimation
/budget-planEstimate budget for keywords via Keyword Planner

Safety Model

AdLoop manages real ad spend, so safety is not optional.

  • Two-step writes. Every mutation returns a preview first. A separate confirm_and_apply call is required to execute.
  • Dry-run by default. Even confirm_and_apply defaults to dry_run=true. Real changes require explicit dry_run=false.
  • Two-phase apply (optional). With safety.two_phase_apply: true, confirm_and_apply refuses dry_run=false until the plan has completed one dry-run pass — preview-then-apply becomes server-enforced instead of a convention.
  • Budget caps. Configurable maximum daily budget — the server rejects anything above the cap.
  • Audit log. Every operation (including dry runs) is logged to ~/.adloop/audit.log.
  • New campaigns and ads are PAUSED. Nothing goes live without manual enablement.
  • Destructive ops are flagged for double confirmation. Removing entities or large budget increases come back with extra warnings, and the AI is instructed to confirm twice.
  • Broad Match without Smart Bidding caught. The #1 cause of wasted ad spend: draft_campaign refuses BROAD keywords unless the campaign uses Smart Bidding, and draft_keywords / draft_ad_group mark them as dangerous in the preview, so the AI has to raise it before anything is applied.
  • Pre-write validation. Before any write, the AI checks bidding strategy, conversion tracking status, and quality scores. If the campaign is fundamentally broken, AdLoop warns you instead of making things worse.
  • Structured error handling. All tools return actionable error messages with hints instead of raw exceptions. Auth errors include specific re-authorization steps.
  • API version pinning. The Google Ads API version is pinned to prevent silent breaking changes from library updates. health_check warns when a newer version is available.
  • Ask mode compatibility. Read tools declare readOnlyHint so they work in Cursor's Ask mode without switching to Agent mode.

Setup

AdLoop uses your own (free) Google Cloud project for OAuth. The adloop init wizard walks you through it — a one-time setup of about 5 minutes, with no shared user caps and no waiting on anyone's verification review. AdLoop does not ship built-in OAuth credentials.

Prefer zero setup? AdLoop Cloud is the hosted version: connect Google in two clicks — no Cloud project, no API access application, EU-hosted.

(Upgrading from ≤0.9 with built-in credentials? Those sign-ins were retired in 0.10 — run adloop init once to switch to your own project.)

[!IMPORTANT] Seeing deleted_client: The OAuth client was deleted. or invalid_client? The shared Google Cloud project behind AdLoop ≤0.9's bundled credentials has been shut down, so its stored sign-ins no longer refresh. Two ways forward: AdLoop Cloud (connect Google in two clicks, nothing to configure) or stay self-hosted with pip install -U adloop && adloop init to set up your own free Google Cloud project. Details in the pinned issue (#49).

Install

From PyPI:

pip install adloop
adloop init

From source:

git clone https://github.com/kLOsk/adloop.git
cd adloop
uv sync
uv run adloop init

What adloop init does

The wizard walks you through:

  • Google Cloud setup — creates a project, enables the three APIs, generates an OAuth client (see Custom Google Cloud Project Setup below for the exact steps the wizard refers you to)
  • Google Ads API access — applied for on your Cloud project's Google Ads API Overview page; the legacy developer-token prompt can be left empty
  • MCC Account ID — optional, only if you reach several accounts through a Manager Account
  • OAuth sign-in — opens a browser to sign in with Google (or prints a URL for headless servers)
  • Auto-discovers your accounts — finds your GA4 properties and Ads accounts automatically
  • Optional services — pin a GTM container, a Search Console property (both auto-discovered too), and a PageSpeed API key; skip any of them with Enter
  • Safety defaults — budget cap and dry-run preference
  • Toolsets — optionally expose only part of the tool catalog to your AI client (see Toolsets)
  • Editor config snippets — prints MCP configuration for both Cursor and Claude Code, including your toolset selection

Requirements

  • Python 3.11+
  • A Google Ads account (a Manager Account is optional; you only need one to reach several accounts through one login)
  • Google Ads API access on your Google Cloud project (see below)

Google Ads API access

Since 9 September 2026, Google Ads API access belongs to the Google Cloud project that owns your OAuth client. Developer tokens are sunset: the API ignores the header, the wizard's token prompt is optional, and you no longer need a Manager Account to get access.

Apply on your project's Google Ads API Overview page in the Cloud Console:

LevelHow to get itWhat it allows
TestAutomatic when you enable the Google Ads APITest accounts only, not production accounts. CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION (or DEVELOPER_TOKEN_NOT_APPROVED on older API versions) means you are here.
Explorer"Upgrade access level" → apply; usually granted automatically2,880 operations/day on production accounts. Enough to get started.
BasicApply; reviewed automatically within minutes once the project is brand-verified (OAuth consent screen set to External and published "In production")15,000 operations/day.
StandardApply; manual review, about ten business daysHigher limits; Google's RMF policy applies

Projects on a Cloud free trial or with billing disabled are refused Explorer and Basic; use a project with billing set up.

Getting CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION? Your project is at Test level. Open the Overview page above and apply for Explorer access.

Headless Servers

Running on a server without a browser (VMs, Docker, SSH)? The wizard automatically detects this and falls back to a manual flow: it prints an authorization URL you can open on any device, then you paste the redirect URL back into the terminal.

Custom Google Cloud Project Setup

The wizard refers to these steps — do them in your browser before running adloop init (or while it waits at the OAuth prompt).

Step 1 — Google Cloud Project

  • Go to console.cloud.google.com and create a new project
  • Enable these three APIs (search for each in the API Library):
    • Google Analytics Data API — for GA4 reports and events
    • Google Analytics Admin API — for listing GA4 properties
    • Google Ads API — for all ads operations

Step 2 — OAuth Credentials

  • In your Google Cloud project, go to APIs & Services → Credentials
  • Click Create Credentials → OAuth client ID
  • Select Desktop app as the application type, give it any name
  • Download the JSON file and save it as ~/.adloop/credentials.json

Service accounts are also supported — just place the service account key JSON at the same credentials_path. AdLoop detects the file type automatically.

Step 3 — Connect to Your Editor

Cursor — Add to your project's .cursor/mcp.json:

{
  "mcpServers": {
    "adloop": {
      "command": "/absolute/path/to/adloop/.venv/bin/python",
      "args": ["-m", "adloop"]
    }
  }
}

Then copy .cursor/rules/adloop.mdc from this repo into your project's .cursor/rules/ directory.

Claude Code — Run:

claude mcp add --transport stdio adloop -- /absolute/path/to/adloop/.venv/bin/python -m adloop

Or add to your project's .mcp.json:

{
  "mcpServers": {
    "adloop": {
      "command": "/absolute/path/to/adloop/.venv/bin/python",
      "args": ["-m", "adloop"]
    }
  }
}

Then install the orchestration rules + slash commands globally so every Claude Code session inherits them:

adloop install-rules

This writes a managed block to ~/.claude/CLAUDE.md and copies the slash commands (prefixed adloop-*) into ~/.claude/commands/. The block is delimited by sentinel comments so it's safe to run multiple times — re-running just refreshes the content. Two install modes:

  • inline (default) — full rules embedded in ~/.claude/CLAUDE.md. Reliable but adds ~10K tokens to every Claude Code session.
  • lazy (adloop install-rules --lazy) — small directive in CLAUDE.md pointing at ~/.claude/rules/adloop.md. Cheaper baseline cost; the LLM reads the rules file only when AdLoop tools are in scope.

To refresh after upgrading AdLoop: adloop update-rules. To remove cleanly: adloop uninstall-rules — only the managed block and adloop-* commands are touched, never your own content.

If you'd rather manage things by hand instead, copy .claude/rules/adloop.md and .claude/commands/ from this repo into your project's .claude/ directory.

Claude Desktop / claude.ai has no programmatic rules location. Run adloop install-rules and it will print the rules content for you to paste into Project settings → Custom instructions on claude.ai.

Use It

Ask your AI assistant things like:

  • "How are my Google Ads campaigns performing this month?"
  • "Which search terms are wasting budget? Add them as negative keywords."
  • "My sign-up conversions dropped — check GA4 and Ads to find out why."
  • "Draft a new responsive search ad for my main campaign."
  • "Which landing pages get paid traffic but don't convert?"
  • "Is my tracking set up correctly? Compare my codebase events against GA4."
  • "Audit my Google Tag Manager container — which conversions are being captured and where are the gaps?"
  • "What keywords should I target for [product]? Find ideas and estimate the budget."
  • "How much budget would I need for these keywords in Germany?"
  • "Create a new search campaign for [product feature] with a €20/day budget."

Configuration Reference

All configuration lives in ~/.adloop/config.yaml. See config.yaml.example for a documented template.

SectionKeyDefaultDescription
googleproject_id(empty)Google Cloud project ID (only needed with custom credentials)
googlecredentials_path(empty)Path to OAuth client JSON or service account key. Empty = ~/.adloop/credentials.json, else Application Default Credentials.
googletoken_path~/.adloop/token.jsonWhere to store the OAuth token (auto-created)
ga4property_id—Your GA4 property ID (auto-discovered by adloop init)
adsdeveloper_token—Legacy, optional: API access belongs to your Cloud project since Sept 2026
adscustomer_id—Default Google Ads customer ID (auto-discovered by adloop init)
adslogin_customer_id—Your MCC account ID
redditclient_id / client_secret(empty)Your Reddit developer app (Business Manager → Developer Application)
redditad_account_id(empty)Default Reddit ad account for every Reddit tool (picked by adloop init)
redditusername(empty)Your Reddit username, used only in the User-Agent Reddit requires
reddittoken_path~/.adloop/reddit_token.jsonWhere the Reddit refresh token is stored
safetymax_daily_budget50.00Maximum allowed daily budget per campaign
safetyrequire_dry_runtrueForce all writes to dry-run mode
safetytwo_phase_applyfalseRefuse real applies until the plan had a dry-run pass
safetyblocked_operations[]Operations to block entirely

Toolsets — trim the context footprint

Most MCP clients (claude.ai, ChatGPT, Cursor, …) load every tool schema into the model's context at the start of every conversation. AdLoop's full catalog costs roughly 18k tokens per session that way — paid before you type a word. If you only use part of AdLoop, expose a subset with the ADLOOP_TOOLSETS environment variable in your MCP client's env block (the adloop init wizard offers this and writes it into the snippets for you):

"env": { "ADLOOP_TOOLSETS": "ads,ga4" }
ToolsetCovers
adsGoogle Ads reads, writes, and planning (Keyword Planner)
ga4Google Analytics reports, realtime, key events
trackingCross-channel attribution + tracking code generation
gtmGoogle Tag Manager audits, reads, and opt-in writes
gscSearch Console reads
webPageSpeed / Core Web Vitals
merchantMerchant Center feed health
redditReddit Ads reads, writes, and planning

health_check and confirm_and_apply are always included, whatever you select. Unset = the full catalog; unknown names fail at startup with the valid list. The effect is real: ads,ga4 drops the session cost to ~13k tokens, and a ga4-only client pays ~2k — nearly 90% less. Toolsets are per client, not per install: one AdLoop config can serve a trimmed Cursor and a full-catalog Claude Code side by side.

On AdLoop Cloud, the same feature is per API key: pick toolsets when creating a key in the dashboard, and that key's tools/list is trimmed server-side for whichever AI client uses it.

Project Structure

src/adloop/
├── __init__.py        # Entry point — routes 'adloop init' to wizard, otherwise starts MCP server
├── server.py          # FastMCP server — every tool registration with safety annotations and toolset tags
├── config.py          # Config loader (~/.adloop/config.yaml)
├── auth.py            # OAuth 2.0 flow (user-supplied credentials, headless fallback) + service accounts; GA4 / Ads / GTM scopes
├── cli.py             # Interactive 'adloop init' setup wizard
├── crossref.py        # Cross-reference tools (GA4 + Ads + GTM combined analysis)
├── tracking.py        # Tracking validation + code generation tools
├── ga4/
│   ├── client.py      # GA4 Data + Admin API clients
│   ├── reports.py     # Account summaries, reports, realtime
│   └── tracking.py    # Event discovery
├── ads/
│   ├── client.py      # Google Ads API client (version-pinned) + retry/backoff for rate limits
│   ├── gaql.py        # GAQL query execution with human-readable error parsing
│   ├── read.py        # Campaign, ad, keyword, search term, negative keyword, shared sets, recommendations, audience reads
│   ├── pmax.py        # Performance Max tools — campaign/asset group performance, asset labels, top combinations
│   ├── write.py       # Draft campaign, RSA, keywords; pause, enable, remove, confirm
│   └── forecast.py    # Budget estimation + keyword discovery via Keyword Planner API
├── gtm/
│   ├── client.py      # Google Tag Manager API v2 client
│   └── read.py        # Live container fetching, tag/trigger/variable parsing, workspace diff, version history
├── reddit/
│   ├── auth.py        # Reddit OAuth2 (own app, permanent refresh token, loopback flow for adloop init)
│   ├── client.py      # Reddit Ads API v3 REST client — bearer refresh, per-user rate limits, pagination
│   ├── read.py        # Accounts, campaigns, ad groups, ads, performance reports, pixels, targeting lookups
│   └── write.py       # Draft/preflight/apply for status, budget, bid, targeting, and PAUSED creation
└── safety/
    ├── guards.py      # Budget caps, bid limits, blocked operations, Broad Match safety
    ├── preview.py     # Change plans and previews
    └── audit.py       # Mutation audit logging

Roadmap

What's been shipped and what's next:

  • GA4 read tools ✓
  • Google Ads read + write tools with safety layer ✓
  • Cross-reference intelligence (campaign→conversion mapping, landing page analysis, attribution comparison) ✓
  • Tracking utilities (validate events against GA4, generate gtag code) ✓
  • Budget estimation + keyword discovery via Keyword Planner ✓
  • Shared negative keyword lists (SharedSet API) ✓
  • Retry/backoff for API rate limits ✓
  • Setup wizard (adloop init) ✓
  • Claude Code support ✓ — CLAUDE.md, .mcp.json, .claude/rules/, .claude/commands/, CLI wizard snippets
  • Claude Desktop one-click install — adloop install claude-desktop (and/or a .dxt extension bundle) that writes the AdLoop MCP entry into claude_desktop_config.json automatically, so Claude Desktop + Cowork users don't have to hand-edit JSON
  • PyPI package ✓ — pip install adloop
  • AdLoop Cloud ✓ — the hosted version: no Google Cloud project, no API access application, connect Google in two clicks (EU-hosted, GDPR-first)
  • Headless server support ✓ — manual URL copy-paste flow for servers without a browser
  • Behavioral eval suites ✓ — prompt-and-expectation tests in tests/evals/ covering read, write, tracking, and planning workflows
  • Google Tag Manager integration ✓ — read tools for tags, triggers, variables, workspaces, and version history, plus the audit_event_coverage three-way join across codebase events, GTM tags, and GA4 actual fires
  • Community launch — HN, Indie Hackers, r/cursor, Twitter
  • Video walkthrough

Contributing

See CONTRIBUTING.md for guidelines. The short version: open an issue describing your situation first, then submit a PR if you want to build it.

License

MIT — see LICENSE.

Privacy

The open-source version runs entirely on your machine. No data is collected, stored, or transmitted to any server. See PRIVACY.md for the full privacy policy. AdLoop Cloud has its own privacy policy and DPA.

If AdLoop helps you run Google Ads, GA4, and tracking code from one place — give it a star or try the hosted version.

Made by @kLOsk | AdLoop Cloud | Privacy Policy

Keywords

mcp

FAQs

Related posts