adloop 0.10.0__tar.gz → 0.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {adloop-0.10.0 → adloop-0.11.0}/PKG-INFO +64 -11
- {adloop-0.10.0 → adloop-0.11.0}/README.md +62 -10
- {adloop-0.10.0 → adloop-0.11.0}/pyproject.toml +2 -1
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/__init__.py +5 -1
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ads/read.py +400 -5
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ads/write.py +421 -3
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/auth.py +30 -1
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/crossref.py +262 -0
- adloop-0.11.0/src/adloop/gtm/__init__.py +1 -0
- adloop-0.11.0/src/adloop/gtm/client.py +20 -0
- adloop-0.11.0/src/adloop/gtm/read.py +648 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/rules/adloop.md +56 -4
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/rules/commands/analyze-performance.md +2 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/rules/commands/optimize-campaign.md +2 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/server.py +405 -2
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/__main__.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/_mcp_patches.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ads/__init__.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ads/client.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ads/currency.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ads/enums.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ads/forecast.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ads/gaql.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ads/pmax.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/cli.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/config.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/diagnostics.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ga4/__init__.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ga4/client.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ga4/reports.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/ga4/tracking.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/rules/__init__.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/rules/commands/budget-plan.md +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/rules/commands/create-ad.md +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/rules/commands/create-campaign.md +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/rules/commands/diagnose-tracking.md +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/rules_install.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/runtime.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/safety/__init__.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/safety/audit.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/safety/guards.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/safety/preview.py +0 -0
- {adloop-0.10.0 → adloop-0.11.0}/src/adloop/tracking.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: adloop
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: The AI command center for Google Ads, GA4, and tracking code.
|
|
5
5
|
Keywords: mcp,google-ads,google-analytics,ga4,cursor,marketing
|
|
6
6
|
Author: Daniel Klose
|
|
@@ -10,6 +10,7 @@ Requires-Dist: fastmcp>=3.0.0
|
|
|
10
10
|
Requires-Dist: google-ads>=29.0.0
|
|
11
11
|
Requires-Dist: google-analytics-data>=0.20.0
|
|
12
12
|
Requires-Dist: google-analytics-admin>=0.27.0
|
|
13
|
+
Requires-Dist: google-api-python-client>=2.100.0
|
|
13
14
|
Requires-Dist: google-auth-oauthlib>=1.0.0
|
|
14
15
|
Requires-Dist: pyyaml>=6.0
|
|
15
16
|
Requires-Dist: pytest>=8.0 ; extra == 'dev'
|
|
@@ -38,12 +39,31 @@ Description-Content-Type: text/markdown
|
|
|
38
39
|
|
|
39
40
|
An MCP server that gives your AI assistant read + write access to Google Ads and GA4 — with safety guardrails that prevent accidental spend.
|
|
40
41
|
|
|
41
|
-
`pip install adloop`
|
|
42
|
+
**[☁️ Skip the setup — use AdLoop Cloud (free beta)](https://getadloop.com)** · or self-host: `pip install adloop`
|
|
42
43
|
|
|
43
44
|
</div>
|
|
44
45
|
|
|
46
|
+
> [!TIP]
|
|
47
|
+
> **[AdLoop Cloud](https://getadloop.com) is the hosted version of this project — live now, free during beta (limited seats).** Connect Google in two clicks and use the same 61 tools from claude.ai, ChatGPT, Claude Code, Cursor, or Gemini. No Google Cloud project, no developer token, no OAuth verification wait. EU-hosted, GDPR-first, DPA included.
|
|
48
|
+
|
|
45
49
|
---
|
|
46
50
|
|
|
51
|
+
## Cloud or Self-Hosted?
|
|
52
|
+
|
|
53
|
+
Both versions run the same 61 tools with the same safety model. The difference is who handles the plumbing:
|
|
54
|
+
|
|
55
|
+
| | ☁️ [AdLoop Cloud](https://getadloop.com) | 🛠️ Self-hosted (this repo) |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| **Setup** | Connect Google in two clicks | ~5 min: own Google Cloud project + `adloop init` |
|
|
58
|
+
| **Google Cloud project** | Not needed | Required (free) |
|
|
59
|
+
| **Ads developer token** | Not needed | Required (from your MCC) |
|
|
60
|
+
| **Works with** | claude.ai, ChatGPT, Claude Code, Cursor, Gemini | Claude Code, Cursor, Claude Desktop, any local MCP client |
|
|
61
|
+
| **Where your data flows** | EU servers (Germany), GDPR-first, DPA included | 100% your machine — nothing leaves it |
|
|
62
|
+
| **Updates** | Automatic | `pip install -U adloop` |
|
|
63
|
+
| **Price** | Free during beta | Free forever (MIT) |
|
|
64
|
+
|
|
65
|
+
**Not sure? [Start with Cloud](https://getadloop.com)** — 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.
|
|
66
|
+
|
|
47
67
|
## What It Solves
|
|
48
68
|
|
|
49
69
|
AdLoop exists because managing Google Ads alongside your code is a mess. These are the specific problems it handles:
|
|
@@ -58,6 +78,8 @@ AdLoop exists because managing Google Ads alongside your code is a mess. These a
|
|
|
58
78
|
|
|
59
79
|
- **"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.
|
|
60
80
|
|
|
81
|
+
- **"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.
|
|
82
|
+
|
|
61
83
|
- **"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.
|
|
62
84
|
|
|
63
85
|
## Built From Real Usage
|
|
@@ -66,9 +88,9 @@ Every tool exists because of an actual problem hit while running real Google Ads
|
|
|
66
88
|
|
|
67
89
|
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.
|
|
68
90
|
|
|
69
|
-
## All
|
|
91
|
+
## All 61 Tools
|
|
70
92
|
|
|
71
|
-
> **Quick start:** `pip install adloop` or `git clone https://github.com/kLOsk/adloop.git && cd adloop && uv sync && uv run adloop init`
|
|
93
|
+
> **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](https://getadloop.com)
|
|
72
94
|
|
|
73
95
|
### Diagnostics
|
|
74
96
|
|
|
@@ -103,8 +125,11 @@ The best features come from real workflows. If you're using AdLoop and find your
|
|
|
103
125
|
| `get_asset_performance` | Per-asset details for PMax — field type, serving status, content |
|
|
104
126
|
| `get_detailed_asset_performance` | Top-performing asset combinations — which headline+description+image combos Google selects most |
|
|
105
127
|
| `get_audience_performance` | Audience segment performance — remarketing, in-market, affinity, demographics |
|
|
128
|
+
| `get_demographic_targeting` | List demographic criteria (age/gender/parental status/income) on an ad group or campaign |
|
|
106
129
|
| `run_gaql` | Arbitrary GAQL queries for anything else |
|
|
107
130
|
|
|
131
|
+
> **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.
|
|
132
|
+
|
|
108
133
|
### Cross-Reference Tools (GA4 + Ads Combined)
|
|
109
134
|
|
|
110
135
|
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.
|
|
@@ -122,6 +147,27 @@ These tools call both APIs internally and return unified results with auto-gener
|
|
|
122
147
|
| `validate_tracking` | Compare event names found in your codebase against what GA4 actually records. Returns matched, missing, and unexpected events with diagnostics. |
|
|
123
148
|
| `generate_tracking_code` | Generate ready-to-paste GA4 gtag JavaScript for any event, with recommended parameters for well-known events (sign_up, purchase, etc.) and optional trigger wrappers. |
|
|
124
149
|
|
|
150
|
+
### Google Tag Manager Tools
|
|
151
|
+
|
|
152
|
+
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.
|
|
153
|
+
|
|
154
|
+
| Tool | What It Does |
|
|
155
|
+
|------|-------------|
|
|
156
|
+
| `audit_event_coverage` | **The 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. |
|
|
157
|
+
| `list_gtm_accounts` | Discover accessible GTM accounts |
|
|
158
|
+
| `list_gtm_containers` | List containers under an account — returns numeric `container_id` (needed by other tools), public `GTM-XXXXXXX` ID, and usage context (web/iOS/Android/server) |
|
|
159
|
+
| `list_gtm_tags` | Every tag in the live container with parsed event names and resolved firing/blocking trigger names |
|
|
160
|
+
| `get_gtm_tag` | Full raw config for a single tag — every parameter, firing/blocking triggers with filter conditions, priority, pause status, sampling |
|
|
161
|
+
| `list_gtm_triggers` | Every 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. |
|
|
162
|
+
| `get_gtm_trigger` | Full 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 |
|
|
163
|
+
| `list_gtm_variables` | Custom variables (data layer, constants, JS) plus enabled built-in variables |
|
|
164
|
+
| `list_gtm_workspaces` | List drafts (workspaces) under a container — workspace IDs are needed by `get_gtm_workspace_diff` |
|
|
165
|
+
| `get_gtm_workspace_diff` | Drafted-but-not-published changes — common cause of "I edited a tag but nothing happened". Returns `is_clean: true` when nothing is pending. |
|
|
166
|
+
| `list_gtm_versions` | Publish history with version IDs and entity counts. Use to correlate a metric drop with a recent publish. |
|
|
167
|
+
| `get_gtm_version` | Full metadata + tag/trigger names for a single historical container version |
|
|
168
|
+
|
|
169
|
+
> **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.
|
|
170
|
+
|
|
125
171
|
### Planning Tools
|
|
126
172
|
|
|
127
173
|
| Tool | What It Does |
|
|
@@ -145,6 +191,8 @@ All write operations follow a **draft → preview → confirm** workflow. Nothin
|
|
|
145
191
|
| `draft_image_assets` | Create campaign image assets from local PNG, JPEG, or GIF files. |
|
|
146
192
|
| `draft_keywords` | Propose keyword additions with match types. Proactively checks bidding strategy — blocks BROAD match on Manual CPC campaigns. |
|
|
147
193
|
| `add_negative_keywords` | Propose negative keywords directly on a campaign |
|
|
194
|
+
| `add_negative_locations` | Propose negative geo exclusions on a campaign — exclude cities/regions while keeping broader positive targets |
|
|
195
|
+
| `draft_demographic_targeting` | Propose demographic criteria (age, gender, parental status, income) — exclusions by default |
|
|
148
196
|
| `propose_negative_keyword_list` | Draft a shared negative keyword list (SharedSet) and attach it to a campaign — reusable across multiple campaigns |
|
|
149
197
|
| `pause_entity` | Pause a campaign, ad group, ad, or keyword |
|
|
150
198
|
| `enable_entity` | Re-enable a paused entity |
|
|
@@ -344,6 +392,7 @@ Ask your AI assistant things like:
|
|
|
344
392
|
- *"Draft a new responsive search ad for my main campaign."*
|
|
345
393
|
- *"Which landing pages get paid traffic but don't convert?"*
|
|
346
394
|
- *"Is my tracking set up correctly? Compare my codebase events against GA4."*
|
|
395
|
+
- *"Audit my Google Tag Manager container — which conversions are being captured and where are the gaps?"*
|
|
347
396
|
- *"What keywords should I target for [product]? Find ideas and estimate the budget."*
|
|
348
397
|
- *"How much budget would I need for these keywords in Germany?"*
|
|
349
398
|
- *"Create a new search campaign for [product feature] with a €20/day budget."*
|
|
@@ -370,11 +419,11 @@ All configuration lives in `~/.adloop/config.yaml`. See [`config.yaml.example`](
|
|
|
370
419
|
```
|
|
371
420
|
src/adloop/
|
|
372
421
|
├── __init__.py # Entry point — routes 'adloop init' to wizard, otherwise starts MCP server
|
|
373
|
-
├── server.py # FastMCP server —
|
|
422
|
+
├── server.py # FastMCP server — 61 tool registrations with safety annotations
|
|
374
423
|
├── config.py # Config loader (~/.adloop/config.yaml)
|
|
375
|
-
├── auth.py # OAuth 2.0 flow (user-supplied credentials, headless fallback) + service accounts
|
|
424
|
+
├── auth.py # OAuth 2.0 flow (user-supplied credentials, headless fallback) + service accounts; GA4 / Ads / GTM scopes
|
|
376
425
|
├── cli.py # Interactive 'adloop init' setup wizard
|
|
377
|
-
├── crossref.py # Cross-reference tools (GA4 + Ads combined analysis)
|
|
426
|
+
├── crossref.py # Cross-reference tools (GA4 + Ads + GTM combined analysis)
|
|
378
427
|
├── tracking.py # Tracking validation + code generation tools
|
|
379
428
|
├── ga4/
|
|
380
429
|
│ ├── client.py # GA4 Data + Admin API clients
|
|
@@ -387,6 +436,9 @@ src/adloop/
|
|
|
387
436
|
│ ├── pmax.py # Performance Max tools — campaign/asset group performance, asset labels, top combinations
|
|
388
437
|
│ ├── write.py # Draft campaign, RSA, keywords; pause, enable, remove, confirm
|
|
389
438
|
│ └── forecast.py # Budget estimation + keyword discovery via Keyword Planner API
|
|
439
|
+
├── gtm/
|
|
440
|
+
│ ├── client.py # Google Tag Manager API v2 client
|
|
441
|
+
│ └── read.py # Live container fetching, tag/trigger/variable parsing, workspace diff, version history
|
|
390
442
|
└── safety/
|
|
391
443
|
├── guards.py # Budget caps, bid limits, blocked operations, Broad Match safety
|
|
392
444
|
├── preview.py # Change plans and previews
|
|
@@ -408,9 +460,10 @@ What's been shipped and what's next:
|
|
|
408
460
|
- ~~Claude Code support~~ ✓ — `CLAUDE.md`, `.mcp.json`, `.claude/rules/`, `.claude/commands/`, CLI wizard snippets
|
|
409
461
|
- **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
|
|
410
462
|
- ~~PyPI package~~ ✓ — `pip install adloop`
|
|
411
|
-
-
|
|
463
|
+
- ~~[AdLoop Cloud](https://getadloop.com)~~ ✓ — the hosted version, live in beta: no Google Cloud project, no developer token, connect Google in two clicks (EU-hosted, GDPR-first)
|
|
412
464
|
- ~~Headless server support~~ ✓ — manual URL copy-paste flow for servers without a browser
|
|
413
465
|
- ~~Behavioral eval suites~~ ✓ — 28 prompt-and-expectation tests covering read, write, tracking, and planning workflows
|
|
466
|
+
- ~~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
|
|
414
467
|
- **Community launch** — HN, Indie Hackers, r/cursor, Twitter
|
|
415
468
|
- **Video walkthrough**
|
|
416
469
|
|
|
@@ -424,14 +477,14 @@ MIT — see [LICENSE](LICENSE).
|
|
|
424
477
|
|
|
425
478
|
## Privacy
|
|
426
479
|
|
|
427
|
-
|
|
480
|
+
The open-source version runs entirely on your machine. No data is collected, stored, or transmitted to any server. See [PRIVACY.md](PRIVACY.md) for the full privacy policy. AdLoop Cloud has its own [privacy policy](https://getadloop.com/datenschutz) and [DPA](https://getadloop.com/avv).
|
|
428
481
|
|
|
429
482
|
---
|
|
430
483
|
|
|
431
484
|
<div align="center">
|
|
432
485
|
|
|
433
|
-
**If AdLoop helps you run Google Ads, GA4, and tracking code from one place — [give it a star](https://github.com/kLOsk/adloop).**
|
|
486
|
+
**If AdLoop helps you run Google Ads, GA4, and tracking code from one place — [give it a star](https://github.com/kLOsk/adloop) or [try the hosted version](https://getadloop.com).**
|
|
434
487
|
|
|
435
|
-
Made by [@kLOsk](https://github.com/kLOsk) | [Privacy Policy](PRIVACY.md)
|
|
488
|
+
Made by [@kLOsk](https://github.com/kLOsk) | [AdLoop Cloud](https://getadloop.com) | [Privacy Policy](PRIVACY.md)
|
|
436
489
|
|
|
437
490
|
</div>
|
|
@@ -14,12 +14,31 @@
|
|
|
14
14
|
|
|
15
15
|
An MCP server that gives your AI assistant read + write access to Google Ads and GA4 — with safety guardrails that prevent accidental spend.
|
|
16
16
|
|
|
17
|
-
`pip install adloop`
|
|
17
|
+
**[☁️ Skip the setup — use AdLoop Cloud (free beta)](https://getadloop.com)** · or self-host: `pip install adloop`
|
|
18
18
|
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
|
+
> [!TIP]
|
|
22
|
+
> **[AdLoop Cloud](https://getadloop.com) is the hosted version of this project — live now, free during beta (limited seats).** Connect Google in two clicks and use the same 61 tools from claude.ai, ChatGPT, Claude Code, Cursor, or Gemini. No Google Cloud project, no developer token, no OAuth verification wait. EU-hosted, GDPR-first, DPA included.
|
|
23
|
+
|
|
21
24
|
---
|
|
22
25
|
|
|
26
|
+
## Cloud or Self-Hosted?
|
|
27
|
+
|
|
28
|
+
Both versions run the same 61 tools with the same safety model. The difference is who handles the plumbing:
|
|
29
|
+
|
|
30
|
+
| | ☁️ [AdLoop Cloud](https://getadloop.com) | 🛠️ Self-hosted (this repo) |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| **Setup** | Connect Google in two clicks | ~5 min: own Google Cloud project + `adloop init` |
|
|
33
|
+
| **Google Cloud project** | Not needed | Required (free) |
|
|
34
|
+
| **Ads developer token** | Not needed | Required (from your MCC) |
|
|
35
|
+
| **Works with** | claude.ai, ChatGPT, Claude Code, Cursor, Gemini | Claude Code, Cursor, Claude Desktop, any local MCP client |
|
|
36
|
+
| **Where your data flows** | EU servers (Germany), GDPR-first, DPA included | 100% your machine — nothing leaves it |
|
|
37
|
+
| **Updates** | Automatic | `pip install -U adloop` |
|
|
38
|
+
| **Price** | Free during beta | Free forever (MIT) |
|
|
39
|
+
|
|
40
|
+
**Not sure? [Start with Cloud](https://getadloop.com)** — 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.
|
|
41
|
+
|
|
23
42
|
## What It Solves
|
|
24
43
|
|
|
25
44
|
AdLoop exists because managing Google Ads alongside your code is a mess. These are the specific problems it handles:
|
|
@@ -34,6 +53,8 @@ AdLoop exists because managing Google Ads alongside your code is a mess. These a
|
|
|
34
53
|
|
|
35
54
|
- **"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.
|
|
36
55
|
|
|
56
|
+
- **"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.
|
|
57
|
+
|
|
37
58
|
- **"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.
|
|
38
59
|
|
|
39
60
|
## Built From Real Usage
|
|
@@ -42,9 +63,9 @@ Every tool exists because of an actual problem hit while running real Google Ads
|
|
|
42
63
|
|
|
43
64
|
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.
|
|
44
65
|
|
|
45
|
-
## All
|
|
66
|
+
## All 61 Tools
|
|
46
67
|
|
|
47
|
-
> **Quick start:** `pip install adloop` or `git clone https://github.com/kLOsk/adloop.git && cd adloop && uv sync && uv run adloop init`
|
|
68
|
+
> **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](https://getadloop.com)
|
|
48
69
|
|
|
49
70
|
### Diagnostics
|
|
50
71
|
|
|
@@ -79,8 +100,11 @@ The best features come from real workflows. If you're using AdLoop and find your
|
|
|
79
100
|
| `get_asset_performance` | Per-asset details for PMax — field type, serving status, content |
|
|
80
101
|
| `get_detailed_asset_performance` | Top-performing asset combinations — which headline+description+image combos Google selects most |
|
|
81
102
|
| `get_audience_performance` | Audience segment performance — remarketing, in-market, affinity, demographics |
|
|
103
|
+
| `get_demographic_targeting` | List demographic criteria (age/gender/parental status/income) on an ad group or campaign |
|
|
82
104
|
| `run_gaql` | Arbitrary GAQL queries for anything else |
|
|
83
105
|
|
|
106
|
+
> **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.
|
|
107
|
+
|
|
84
108
|
### Cross-Reference Tools (GA4 + Ads Combined)
|
|
85
109
|
|
|
86
110
|
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.
|
|
@@ -98,6 +122,27 @@ These tools call both APIs internally and return unified results with auto-gener
|
|
|
98
122
|
| `validate_tracking` | Compare event names found in your codebase against what GA4 actually records. Returns matched, missing, and unexpected events with diagnostics. |
|
|
99
123
|
| `generate_tracking_code` | Generate ready-to-paste GA4 gtag JavaScript for any event, with recommended parameters for well-known events (sign_up, purchase, etc.) and optional trigger wrappers. |
|
|
100
124
|
|
|
125
|
+
### Google Tag Manager Tools
|
|
126
|
+
|
|
127
|
+
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.
|
|
128
|
+
|
|
129
|
+
| Tool | What It Does |
|
|
130
|
+
|------|-------------|
|
|
131
|
+
| `audit_event_coverage` | **The 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. |
|
|
132
|
+
| `list_gtm_accounts` | Discover accessible GTM accounts |
|
|
133
|
+
| `list_gtm_containers` | List containers under an account — returns numeric `container_id` (needed by other tools), public `GTM-XXXXXXX` ID, and usage context (web/iOS/Android/server) |
|
|
134
|
+
| `list_gtm_tags` | Every tag in the live container with parsed event names and resolved firing/blocking trigger names |
|
|
135
|
+
| `get_gtm_tag` | Full raw config for a single tag — every parameter, firing/blocking triggers with filter conditions, priority, pause status, sampling |
|
|
136
|
+
| `list_gtm_triggers` | Every 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. |
|
|
137
|
+
| `get_gtm_trigger` | Full 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 |
|
|
138
|
+
| `list_gtm_variables` | Custom variables (data layer, constants, JS) plus enabled built-in variables |
|
|
139
|
+
| `list_gtm_workspaces` | List drafts (workspaces) under a container — workspace IDs are needed by `get_gtm_workspace_diff` |
|
|
140
|
+
| `get_gtm_workspace_diff` | Drafted-but-not-published changes — common cause of "I edited a tag but nothing happened". Returns `is_clean: true` when nothing is pending. |
|
|
141
|
+
| `list_gtm_versions` | Publish history with version IDs and entity counts. Use to correlate a metric drop with a recent publish. |
|
|
142
|
+
| `get_gtm_version` | Full metadata + tag/trigger names for a single historical container version |
|
|
143
|
+
|
|
144
|
+
> **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.
|
|
145
|
+
|
|
101
146
|
### Planning Tools
|
|
102
147
|
|
|
103
148
|
| Tool | What It Does |
|
|
@@ -121,6 +166,8 @@ All write operations follow a **draft → preview → confirm** workflow. Nothin
|
|
|
121
166
|
| `draft_image_assets` | Create campaign image assets from local PNG, JPEG, or GIF files. |
|
|
122
167
|
| `draft_keywords` | Propose keyword additions with match types. Proactively checks bidding strategy — blocks BROAD match on Manual CPC campaigns. |
|
|
123
168
|
| `add_negative_keywords` | Propose negative keywords directly on a campaign |
|
|
169
|
+
| `add_negative_locations` | Propose negative geo exclusions on a campaign — exclude cities/regions while keeping broader positive targets |
|
|
170
|
+
| `draft_demographic_targeting` | Propose demographic criteria (age, gender, parental status, income) — exclusions by default |
|
|
124
171
|
| `propose_negative_keyword_list` | Draft a shared negative keyword list (SharedSet) and attach it to a campaign — reusable across multiple campaigns |
|
|
125
172
|
| `pause_entity` | Pause a campaign, ad group, ad, or keyword |
|
|
126
173
|
| `enable_entity` | Re-enable a paused entity |
|
|
@@ -320,6 +367,7 @@ Ask your AI assistant things like:
|
|
|
320
367
|
- *"Draft a new responsive search ad for my main campaign."*
|
|
321
368
|
- *"Which landing pages get paid traffic but don't convert?"*
|
|
322
369
|
- *"Is my tracking set up correctly? Compare my codebase events against GA4."*
|
|
370
|
+
- *"Audit my Google Tag Manager container — which conversions are being captured and where are the gaps?"*
|
|
323
371
|
- *"What keywords should I target for [product]? Find ideas and estimate the budget."*
|
|
324
372
|
- *"How much budget would I need for these keywords in Germany?"*
|
|
325
373
|
- *"Create a new search campaign for [product feature] with a €20/day budget."*
|
|
@@ -346,11 +394,11 @@ All configuration lives in `~/.adloop/config.yaml`. See [`config.yaml.example`](
|
|
|
346
394
|
```
|
|
347
395
|
src/adloop/
|
|
348
396
|
├── __init__.py # Entry point — routes 'adloop init' to wizard, otherwise starts MCP server
|
|
349
|
-
├── server.py # FastMCP server —
|
|
397
|
+
├── server.py # FastMCP server — 61 tool registrations with safety annotations
|
|
350
398
|
├── config.py # Config loader (~/.adloop/config.yaml)
|
|
351
|
-
├── auth.py # OAuth 2.0 flow (user-supplied credentials, headless fallback) + service accounts
|
|
399
|
+
├── auth.py # OAuth 2.0 flow (user-supplied credentials, headless fallback) + service accounts; GA4 / Ads / GTM scopes
|
|
352
400
|
├── cli.py # Interactive 'adloop init' setup wizard
|
|
353
|
-
├── crossref.py # Cross-reference tools (GA4 + Ads combined analysis)
|
|
401
|
+
├── crossref.py # Cross-reference tools (GA4 + Ads + GTM combined analysis)
|
|
354
402
|
├── tracking.py # Tracking validation + code generation tools
|
|
355
403
|
├── ga4/
|
|
356
404
|
│ ├── client.py # GA4 Data + Admin API clients
|
|
@@ -363,6 +411,9 @@ src/adloop/
|
|
|
363
411
|
│ ├── pmax.py # Performance Max tools — campaign/asset group performance, asset labels, top combinations
|
|
364
412
|
│ ├── write.py # Draft campaign, RSA, keywords; pause, enable, remove, confirm
|
|
365
413
|
│ └── forecast.py # Budget estimation + keyword discovery via Keyword Planner API
|
|
414
|
+
├── gtm/
|
|
415
|
+
│ ├── client.py # Google Tag Manager API v2 client
|
|
416
|
+
│ └── read.py # Live container fetching, tag/trigger/variable parsing, workspace diff, version history
|
|
366
417
|
└── safety/
|
|
367
418
|
├── guards.py # Budget caps, bid limits, blocked operations, Broad Match safety
|
|
368
419
|
├── preview.py # Change plans and previews
|
|
@@ -384,9 +435,10 @@ What's been shipped and what's next:
|
|
|
384
435
|
- ~~Claude Code support~~ ✓ — `CLAUDE.md`, `.mcp.json`, `.claude/rules/`, `.claude/commands/`, CLI wizard snippets
|
|
385
436
|
- **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
|
|
386
437
|
- ~~PyPI package~~ ✓ — `pip install adloop`
|
|
387
|
-
-
|
|
438
|
+
- ~~[AdLoop Cloud](https://getadloop.com)~~ ✓ — the hosted version, live in beta: no Google Cloud project, no developer token, connect Google in two clicks (EU-hosted, GDPR-first)
|
|
388
439
|
- ~~Headless server support~~ ✓ — manual URL copy-paste flow for servers without a browser
|
|
389
440
|
- ~~Behavioral eval suites~~ ✓ — 28 prompt-and-expectation tests covering read, write, tracking, and planning workflows
|
|
441
|
+
- ~~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
|
|
390
442
|
- **Community launch** — HN, Indie Hackers, r/cursor, Twitter
|
|
391
443
|
- **Video walkthrough**
|
|
392
444
|
|
|
@@ -400,14 +452,14 @@ MIT — see [LICENSE](LICENSE).
|
|
|
400
452
|
|
|
401
453
|
## Privacy
|
|
402
454
|
|
|
403
|
-
|
|
455
|
+
The open-source version runs entirely on your machine. No data is collected, stored, or transmitted to any server. See [PRIVACY.md](PRIVACY.md) for the full privacy policy. AdLoop Cloud has its own [privacy policy](https://getadloop.com/datenschutz) and [DPA](https://getadloop.com/avv).
|
|
404
456
|
|
|
405
457
|
---
|
|
406
458
|
|
|
407
459
|
<div align="center">
|
|
408
460
|
|
|
409
|
-
**If AdLoop helps you run Google Ads, GA4, and tracking code from one place — [give it a star](https://github.com/kLOsk/adloop).**
|
|
461
|
+
**If AdLoop helps you run Google Ads, GA4, and tracking code from one place — [give it a star](https://github.com/kLOsk/adloop) or [try the hosted version](https://getadloop.com).**
|
|
410
462
|
|
|
411
|
-
Made by [@kLOsk](https://github.com/kLOsk) | [Privacy Policy](PRIVACY.md)
|
|
463
|
+
Made by [@kLOsk](https://github.com/kLOsk) | [AdLoop Cloud](https://getadloop.com) | [Privacy Policy](PRIVACY.md)
|
|
412
464
|
|
|
413
465
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "adloop"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.11.0"
|
|
4
4
|
description = "The AI command center for Google Ads, GA4, and tracking code."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -14,6 +14,7 @@ dependencies = [
|
|
|
14
14
|
"google-ads>=29.0.0",
|
|
15
15
|
"google-analytics-data>=0.20.0",
|
|
16
16
|
"google-analytics-admin>=0.27.0",
|
|
17
|
+
"google-api-python-client>=2.100.0",
|
|
17
18
|
"google-auth-oauthlib>=1.0.0",
|
|
18
19
|
"pyyaml>=6.0",
|
|
19
20
|
]
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"""AdLoop — MCP server connecting Google Ads + GA4 + codebase."""
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
try:
|
|
7
|
+
__version__ = version("adloop")
|
|
8
|
+
except PackageNotFoundError: # running from a source tree without install
|
|
9
|
+
__version__ = "0.0.0.dev0"
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
def main() -> None:
|