adloop 0.13.3__tar.gz → 0.15.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.13.3/README.md → adloop-0.15.0/PKG-INFO +82 -25
- adloop-0.13.3/PKG-INFO → adloop-0.15.0/README.md +55 -51
- {adloop-0.13.3 → adloop-0.15.0}/pyproject.toml +5 -3
- {adloop-0.13.3 → adloop-0.15.0}/pyproject.toml.orig +7 -4
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ads/client.py +7 -2
- adloop-0.15.0/src/adloop/ads/conversion_actions.py +558 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ads/enums.py +2 -4
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ads/forecast.py +4 -5
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ads/gaql.py +15 -6
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ads/write.py +339 -20
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/auth.py +23 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/cli.py +136 -13
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/config.py +27 -0
- adloop-0.15.0/src/adloop/reddit/__init__.py +9 -0
- adloop-0.15.0/src/adloop/reddit/auth.py +504 -0
- adloop-0.15.0/src/adloop/reddit/client.py +270 -0
- adloop-0.15.0/src/adloop/reddit/read.py +1344 -0
- adloop-0.15.0/src/adloop/reddit/schedule.py +200 -0
- adloop-0.15.0/src/adloop/reddit/write.py +1684 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/rules/adloop.md +75 -7
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/safety/preview.py +4 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/server.py +1026 -15
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/__init__.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/__main__.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/_mcp_patches.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ads/__init__.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ads/currency.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ads/pmax.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ads/read.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/crossref.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/diagnostics.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ga4/__init__.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ga4/client.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ga4/reports.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ga4/tracking.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/ga4/write.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/gsc/__init__.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/gsc/client.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/gsc/reports.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/gtm/__init__.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/gtm/client.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/gtm/read.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/merchant/__init__.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/merchant/client.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/merchant/read.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/pagespeed.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/rules/__init__.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/rules/commands/analyze-performance.md +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/rules/commands/budget-plan.md +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/rules/commands/create-ad.md +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/rules/commands/create-campaign.md +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/rules/commands/diagnose-tracking.md +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/rules/commands/optimize-campaign.md +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/rules_install.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/runtime.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/safety/__init__.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/safety/audit.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/safety/guards.py +0 -0
- {adloop-0.13.3 → adloop-0.15.0}/src/adloop/tracking.py +0 -0
|
@@ -1,27 +1,54 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: adloop
|
|
3
|
+
Version: 0.15.0
|
|
4
|
+
Summary: The AI command center for Google Ads, Reddit Ads, GA4, and tracking code.
|
|
5
|
+
Keywords: mcp,google-ads,reddit-ads,google-analytics,ga4,cursor,marketing
|
|
6
|
+
Author: Daniel Klose
|
|
7
|
+
Author-email: Daniel Klose <info@daniel-klose.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Requires-Dist: fastmcp>=3.0.0
|
|
10
|
+
Requires-Dist: google-ads>=32.0.0
|
|
11
|
+
Requires-Dist: google-analytics-data>=0.20.0
|
|
12
|
+
Requires-Dist: google-analytics-admin>=0.27.0
|
|
13
|
+
Requires-Dist: google-api-python-client>=2.100.0
|
|
14
|
+
Requires-Dist: google-auth-oauthlib>=1.0.0
|
|
15
|
+
Requires-Dist: google-api-python-client>=2.0.0
|
|
16
|
+
Requires-Dist: pyyaml>=6.0
|
|
17
|
+
Requires-Dist: requests>=2.31
|
|
18
|
+
Requires-Dist: pytest>=8.0 ; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Project-URL: Homepage, https://github.com/kLOsk/adloop
|
|
22
|
+
Project-URL: Repository, https://github.com/kLOsk/adloop
|
|
23
|
+
Project-URL: Issues, https://github.com/kLOsk/adloop/issues
|
|
24
|
+
Project-URL: Changelog, https://github.com/kLOsk/adloop/releases
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
1
28
|
<!-- mcp-name: com.getadloop/adloop -->
|
|
2
29
|
|
|
3
30
|
<div align="center">
|
|
4
31
|
|
|
5
32
|
# AdLoop
|
|
6
33
|
|
|
7
|
-
**The AI command center for Google Ads, GA4, and tracking code.**
|
|
34
|
+
**The AI command center for Google Ads, Reddit Ads, GA4, and tracking code.**
|
|
8
35
|
|
|
9
36
|
[](https://pypi.org/project/adloop/)
|
|
10
37
|
[](LICENSE)
|
|
11
38
|
[](https://www.python.org/downloads/)
|
|
12
39
|
[](https://modelcontextprotocol.io)
|
|
13
|
-
[](https://developers.google.com/google-ads/api/docs/start)
|
|
14
41
|
[](https://developers.google.com/analytics/devguides/reporting/data/v1)
|
|
15
42
|
[](https://github.com/kLOsk/adloop)
|
|
16
43
|
|
|
17
|
-
An MCP server that gives your AI assistant read + write access to Google Ads and GA4 — with safety guardrails that prevent accidental spend.
|
|
44
|
+
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.
|
|
18
45
|
|
|
19
46
|
**[☁️ Skip the setup — use AdLoop Cloud (free plan, no card)](https://getadloop.com)** · or self-host: `pip install adloop`
|
|
20
47
|
|
|
21
48
|
</div>
|
|
22
49
|
|
|
23
50
|
> [!TIP]
|
|
24
|
-
> **[AdLoop Cloud](https://getadloop.com) 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
|
|
51
|
+
> **[AdLoop Cloud](https://getadloop.com) 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.
|
|
25
52
|
|
|
26
53
|
> 📚 **Documentation: [docs.getadloop.com](https://docs.getadloop.com)** — setup guides per AI client, toolsets, the safety model, and troubleshooting for both editions.
|
|
27
54
|
|
|
@@ -35,7 +62,7 @@ Both versions run the same tools with the same safety model. The difference is w
|
|
|
35
62
|
|---|---|---|
|
|
36
63
|
| **Setup** | Connect Google in two clicks | ~5 min: own Google Cloud project + `adloop init` |
|
|
37
64
|
| **Google Cloud project** | Not needed | Required (free) |
|
|
38
|
-
| **Ads
|
|
65
|
+
| **Ads API access** | Not needed | Granted to your Cloud project (no developer token, no MCC since Sept 2026) |
|
|
39
66
|
| **Works with** | claude.ai, ChatGPT, Claude Code, Cursor, Gemini | Claude Code, Cursor, Claude Desktop, any local MCP client |
|
|
40
67
|
| **Where your data flows** | EU servers (Germany), GDPR-first, DPA included | 100% your machine — nothing leaves it |
|
|
41
68
|
| **Updates** | Automatic | `pip install -U adloop` |
|
|
@@ -171,6 +198,27 @@ These tools read the live GTM container and join it with the codebase + GA4 to f
|
|
|
171
198
|
|
|
172
199
|
> **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.
|
|
173
200
|
|
|
201
|
+
### Reddit Ads Tools
|
|
202
|
+
|
|
203
|
+
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`).
|
|
204
|
+
|
|
205
|
+
| Tool | What It Does |
|
|
206
|
+
|------|-------------|
|
|
207
|
+
| `list_reddit_accounts` | Discover businesses and ad accounts (currency, time zone, approval state) |
|
|
208
|
+
| `list_reddit_funding_instruments` | Billing instruments and posting profiles — prerequisites for creating campaigns and ads |
|
|
209
|
+
| `get_reddit_campaigns` / `get_reddit_ad_groups` / `get_reddit_ads` | Structure with configured vs effective status, budgets, bids, pixel, targeting, weekly schedule, rejection reasons; `include_copy` returns each ad's post (headline, body, destination, media) |
|
|
210
|
+
| `get_reddit_performance` | Spend, clicks, CTR, CPC, conversions, CPA, ROAS per account/campaign/ad group/ad, optional breakdown (date, country, community, keyword, placement, …), compact mode with insights |
|
|
211
|
+
| `run_reddit_report` | Raw reports endpoint for any metric (REACH, video, per-event conversions) |
|
|
212
|
+
| `get_reddit_pixels` | Pixels and when each event last fired — flags ad groups optimizing for events the pixel never sent |
|
|
213
|
+
| `search_reddit_targeting` | Communities, interests, geolocations, languages, keyword suggestions, and Reddit's related-community suggestions (by seed communities or website) |
|
|
214
|
+
| `get_reddit_account_history` | Who changed what and when: field, before/after, member |
|
|
215
|
+
| `estimate_reddit_ad_group` | Audience size, delivery estimate and suggested bid range for a planned ad group (Reddit's counterpart of `estimate_budget`) |
|
|
216
|
+
| `pause_reddit_entity` / `enable_reddit_entity` / `remove_reddit_entity` | Status changes through the preview gate (remove = ARCHIVE, irreversible, double-confirmed) |
|
|
217
|
+
| `update_reddit_campaign` / `update_reddit_ad_group` / `update_reddit_ad` | Budget, 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 |
|
|
218
|
+
| `draft_reddit_campaign` / `draft_reddit_ad_group` / `draft_reddit_ad` | Create campaign → ad group (pixel + targeting required) → post + ad, or promote an existing post with `post_id`. Everything is created **PAUSED**. |
|
|
219
|
+
|
|
220
|
+
> **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.
|
|
221
|
+
|
|
174
222
|
### Planning Tools
|
|
175
223
|
|
|
176
224
|
| Tool | What It Does |
|
|
@@ -251,7 +299,7 @@ AdLoop manages real ad spend, so safety is not optional.
|
|
|
251
299
|
|
|
252
300
|
> **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.
|
|
253
301
|
>
|
|
254
|
-
> **Prefer zero setup?** [**AdLoop Cloud**](https://getadloop.com) is the hosted version: connect Google in two clicks — no Cloud project, no
|
|
302
|
+
> **Prefer zero setup?** [**AdLoop Cloud**](https://getadloop.com) is the hosted version: connect Google in two clicks — no Cloud project, no API access application, EU-hosted.
|
|
255
303
|
>
|
|
256
304
|
> *(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.)*
|
|
257
305
|
|
|
@@ -281,8 +329,8 @@ uv run adloop init
|
|
|
281
329
|
The wizard walks you through:
|
|
282
330
|
|
|
283
331
|
1. **Google Cloud setup** — creates a project, enables the three APIs, generates an OAuth client (see [Custom Google Cloud Project Setup](#custom-google-cloud-project-setup) below for the exact steps the wizard refers you to)
|
|
284
|
-
2. **
|
|
285
|
-
3. **MCC Account ID** —
|
|
332
|
+
2. **Google Ads API access** — applied for on your Cloud project's [Google Ads API Overview page](https://console.cloud.google.com/google/ads-apis/overview); the legacy developer-token prompt can be left empty
|
|
333
|
+
3. **MCC Account ID** — optional, only if you reach several accounts through a Manager Account
|
|
286
334
|
4. **OAuth sign-in** — opens a browser to sign in with Google (or prints a URL for headless servers)
|
|
287
335
|
5. **Auto-discovers your accounts** — finds your GA4 properties and Ads accounts automatically
|
|
288
336
|
6. **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
|
|
@@ -293,26 +341,25 @@ The wizard walks you through:
|
|
|
293
341
|
### Requirements
|
|
294
342
|
|
|
295
343
|
- Python 3.11+
|
|
296
|
-
- A Google Ads account
|
|
297
|
-
-
|
|
344
|
+
- A Google Ads account (a Manager Account is optional; you only need one to reach several accounts through one login)
|
|
345
|
+
- Google Ads API access on your Google Cloud project (see below)
|
|
298
346
|
|
|
299
|
-
### Google Ads
|
|
347
|
+
### Google Ads API access
|
|
300
348
|
|
|
301
|
-
|
|
349
|
+
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.
|
|
302
350
|
|
|
303
|
-
|
|
304
|
-
2. In the MCC, go to **Tools & Settings → API Center**
|
|
305
|
-
3. Your **developer token** is shown there. Copy it — the wizard will ask for it.
|
|
351
|
+
Apply on your project's [Google Ads API Overview page](https://console.cloud.google.com/google/ads-apis/overview) in the Cloud Console:
|
|
306
352
|
|
|
307
|
-
|
|
353
|
+
| Level | How to get it | What it allows |
|
|
354
|
+
|-------|---------------|----------------|
|
|
355
|
+
| **Test** | Automatic when you enable the Google Ads API | Test accounts only, **not production accounts**. `CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION` (or `DEVELOPER_TOKEN_NOT_APPROVED` on older API versions) means you are here. |
|
|
356
|
+
| **Explorer** | "Upgrade access level" → apply; usually granted automatically | 2,880 operations/day on production accounts. Enough to get started. |
|
|
357
|
+
| **Basic** | Apply; reviewed automatically within minutes once the project is brand-verified (OAuth consent screen set to External and published "In production") | 15,000 operations/day. |
|
|
358
|
+
| **Standard** | Apply; manual review, about ten business days | Higher limits; Google's RMF policy applies |
|
|
308
359
|
|
|
309
|
-
|
|
310
|
-
|-------|-----------|----------------|
|
|
311
|
-
| **Test Account** | Default for new tokens | Can only access test accounts — **not production accounts**. If you see `DEVELOPER_TOKEN_NOT_APPROVED`, this is why. |
|
|
312
|
-
| **Explorer** | Automatic after first API call with a production account | 2,880 operations/day on production accounts. Enough to get started. |
|
|
313
|
-
| **Basic** | [Apply through API Center](https://ads.google.com/aw/apicenter) | 15,000 operations/day. Apply if you need more. |
|
|
360
|
+
Projects on a Cloud free trial or with billing disabled are refused Explorer and Basic; use a project with billing set up.
|
|
314
361
|
|
|
315
|
-
> **Getting `
|
|
362
|
+
> **Getting `CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION`?** Your project is at Test level. Open the Overview page above and apply for Explorer access.
|
|
316
363
|
|
|
317
364
|
### Headless Servers
|
|
318
365
|
|
|
@@ -417,9 +464,13 @@ All configuration lives in `~/.adloop/config.yaml`. See [`config.yaml.example`](
|
|
|
417
464
|
| `google` | `credentials_path` | *(empty)* | Path to OAuth client JSON or service account key. Empty = `~/.adloop/credentials.json`, else Application Default Credentials. |
|
|
418
465
|
| `google` | `token_path` | `~/.adloop/token.json` | Where to store the OAuth token (auto-created) |
|
|
419
466
|
| `ga4` | `property_id` | — | Your GA4 property ID (auto-discovered by `adloop init`) |
|
|
420
|
-
| `ads` | `developer_token` | — |
|
|
467
|
+
| `ads` | `developer_token` | — | Legacy, optional: API access belongs to your Cloud project since Sept 2026 |
|
|
421
468
|
| `ads` | `customer_id` | — | Default Google Ads customer ID (auto-discovered by `adloop init`) |
|
|
422
469
|
| `ads` | `login_customer_id` | — | Your MCC account ID |
|
|
470
|
+
| `reddit` | `client_id` / `client_secret` | *(empty)* | Your Reddit developer app (Business Manager → Developer Application) |
|
|
471
|
+
| `reddit` | `ad_account_id` | *(empty)* | Default Reddit ad account for every Reddit tool (picked by `adloop init`) |
|
|
472
|
+
| `reddit` | `username` | *(empty)* | Your Reddit username, used only in the User-Agent Reddit requires |
|
|
473
|
+
| `reddit` | `token_path` | `~/.adloop/reddit_token.json` | Where the Reddit refresh token is stored |
|
|
423
474
|
| `safety` | `max_daily_budget` | `50.00` | Maximum allowed daily budget per campaign |
|
|
424
475
|
| `safety` | `require_dry_run` | `true` | Force all writes to dry-run mode |
|
|
425
476
|
| `safety` | `two_phase_apply` | `false` | Refuse real applies until the plan had a dry-run pass |
|
|
@@ -442,6 +493,7 @@ Most MCP clients (claude.ai, ChatGPT, Cursor, …) load **every tool schema into
|
|
|
442
493
|
| `gsc` | Search Console reads |
|
|
443
494
|
| `web` | PageSpeed / Core Web Vitals |
|
|
444
495
|
| `merchant` | Merchant Center feed health |
|
|
496
|
+
| `reddit` | Reddit Ads reads, writes, and planning |
|
|
445
497
|
|
|
446
498
|
`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.
|
|
447
499
|
|
|
@@ -452,7 +504,7 @@ On [AdLoop Cloud](https://getadloop.com), the same feature is per API key: pick
|
|
|
452
504
|
```
|
|
453
505
|
src/adloop/
|
|
454
506
|
├── __init__.py # Entry point — routes 'adloop init' to wizard, otherwise starts MCP server
|
|
455
|
-
├── server.py # FastMCP server —
|
|
507
|
+
├── server.py # FastMCP server — every tool registration with safety annotations and toolset tags
|
|
456
508
|
├── config.py # Config loader (~/.adloop/config.yaml)
|
|
457
509
|
├── auth.py # OAuth 2.0 flow (user-supplied credentials, headless fallback) + service accounts; GA4 / Ads / GTM scopes
|
|
458
510
|
├── cli.py # Interactive 'adloop init' setup wizard
|
|
@@ -472,6 +524,11 @@ src/adloop/
|
|
|
472
524
|
├── gtm/
|
|
473
525
|
│ ├── client.py # Google Tag Manager API v2 client
|
|
474
526
|
│ └── read.py # Live container fetching, tag/trigger/variable parsing, workspace diff, version history
|
|
527
|
+
├── reddit/
|
|
528
|
+
│ ├── auth.py # Reddit OAuth2 (own app, permanent refresh token, loopback flow for adloop init)
|
|
529
|
+
│ ├── client.py # Reddit Ads API v3 REST client — bearer refresh, per-user rate limits, pagination
|
|
530
|
+
│ ├── read.py # Accounts, campaigns, ad groups, ads, performance reports, pixels, targeting lookups
|
|
531
|
+
│ └── write.py # Draft/preflight/apply for status, budget, bid, targeting, and PAUSED creation
|
|
475
532
|
└── safety/
|
|
476
533
|
├── guards.py # Budget caps, bid limits, blocked operations, Broad Match safety
|
|
477
534
|
├── preview.py # Change plans and previews
|
|
@@ -493,7 +550,7 @@ What's been shipped and what's next:
|
|
|
493
550
|
- ~~Claude Code support~~ ✓ — `CLAUDE.md`, `.mcp.json`, `.claude/rules/`, `.claude/commands/`, CLI wizard snippets
|
|
494
551
|
- **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
|
|
495
552
|
- ~~PyPI package~~ ✓ — `pip install adloop`
|
|
496
|
-
- ~~[AdLoop Cloud](https://getadloop.com)~~ ✓ — the hosted version: no Google Cloud project, no
|
|
553
|
+
- ~~[AdLoop Cloud](https://getadloop.com)~~ ✓ — the hosted version: no Google Cloud project, no API access application, connect Google in two clicks (EU-hosted, GDPR-first)
|
|
497
554
|
- ~~Headless server support~~ ✓ — manual URL copy-paste flow for servers without a browser
|
|
498
555
|
- ~~Behavioral eval suites~~ ✓ — 28 prompt-and-expectation tests covering read, write, tracking, and planning workflows
|
|
499
556
|
- ~~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
|
|
@@ -1,53 +1,27 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: adloop
|
|
3
|
-
Version: 0.13.3
|
|
4
|
-
Summary: The AI command center for Google Ads, GA4, and tracking code.
|
|
5
|
-
Keywords: mcp,google-ads,google-analytics,ga4,cursor,marketing
|
|
6
|
-
Author: Daniel Klose
|
|
7
|
-
Author-email: Daniel Klose <info@daniel-klose.com>
|
|
8
|
-
License: MIT
|
|
9
|
-
Requires-Dist: fastmcp>=3.0.0
|
|
10
|
-
Requires-Dist: google-ads>=31.1.0
|
|
11
|
-
Requires-Dist: google-analytics-data>=0.20.0
|
|
12
|
-
Requires-Dist: google-analytics-admin>=0.27.0
|
|
13
|
-
Requires-Dist: google-api-python-client>=2.100.0
|
|
14
|
-
Requires-Dist: google-auth-oauthlib>=1.0.0
|
|
15
|
-
Requires-Dist: google-api-python-client>=2.0.0
|
|
16
|
-
Requires-Dist: pyyaml>=6.0
|
|
17
|
-
Requires-Dist: pytest>=8.0 ; extra == 'dev'
|
|
18
|
-
Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
|
|
19
|
-
Requires-Python: >=3.11
|
|
20
|
-
Project-URL: Homepage, https://github.com/kLOsk/adloop
|
|
21
|
-
Project-URL: Repository, https://github.com/kLOsk/adloop
|
|
22
|
-
Project-URL: Issues, https://github.com/kLOsk/adloop/issues
|
|
23
|
-
Project-URL: Changelog, https://github.com/kLOsk/adloop/releases
|
|
24
|
-
Provides-Extra: dev
|
|
25
|
-
Description-Content-Type: text/markdown
|
|
26
|
-
|
|
27
1
|
<!-- mcp-name: com.getadloop/adloop -->
|
|
28
2
|
|
|
29
3
|
<div align="center">
|
|
30
4
|
|
|
31
5
|
# AdLoop
|
|
32
6
|
|
|
33
|
-
**The AI command center for Google Ads, GA4, and tracking code.**
|
|
7
|
+
**The AI command center for Google Ads, Reddit Ads, GA4, and tracking code.**
|
|
34
8
|
|
|
35
9
|
[](https://pypi.org/project/adloop/)
|
|
36
10
|
[](LICENSE)
|
|
37
11
|
[](https://www.python.org/downloads/)
|
|
38
12
|
[](https://modelcontextprotocol.io)
|
|
39
|
-
[](https://developers.google.com/google-ads/api/docs/start)
|
|
40
14
|
[](https://developers.google.com/analytics/devguides/reporting/data/v1)
|
|
41
15
|
[](https://github.com/kLOsk/adloop)
|
|
42
16
|
|
|
43
|
-
An MCP server that gives your AI assistant read + write access to Google Ads and GA4 — with safety guardrails that prevent accidental spend.
|
|
17
|
+
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.
|
|
44
18
|
|
|
45
19
|
**[☁️ Skip the setup — use AdLoop Cloud (free plan, no card)](https://getadloop.com)** · or self-host: `pip install adloop`
|
|
46
20
|
|
|
47
21
|
</div>
|
|
48
22
|
|
|
49
23
|
> [!TIP]
|
|
50
|
-
> **[AdLoop Cloud](https://getadloop.com) 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
|
|
24
|
+
> **[AdLoop Cloud](https://getadloop.com) 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.
|
|
51
25
|
|
|
52
26
|
> 📚 **Documentation: [docs.getadloop.com](https://docs.getadloop.com)** — setup guides per AI client, toolsets, the safety model, and troubleshooting for both editions.
|
|
53
27
|
|
|
@@ -61,7 +35,7 @@ Both versions run the same tools with the same safety model. The difference is w
|
|
|
61
35
|
|---|---|---|
|
|
62
36
|
| **Setup** | Connect Google in two clicks | ~5 min: own Google Cloud project + `adloop init` |
|
|
63
37
|
| **Google Cloud project** | Not needed | Required (free) |
|
|
64
|
-
| **Ads
|
|
38
|
+
| **Ads API access** | Not needed | Granted to your Cloud project (no developer token, no MCC since Sept 2026) |
|
|
65
39
|
| **Works with** | claude.ai, ChatGPT, Claude Code, Cursor, Gemini | Claude Code, Cursor, Claude Desktop, any local MCP client |
|
|
66
40
|
| **Where your data flows** | EU servers (Germany), GDPR-first, DPA included | 100% your machine — nothing leaves it |
|
|
67
41
|
| **Updates** | Automatic | `pip install -U adloop` |
|
|
@@ -197,6 +171,27 @@ These tools read the live GTM container and join it with the codebase + GA4 to f
|
|
|
197
171
|
|
|
198
172
|
> **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.
|
|
199
173
|
|
|
174
|
+
### Reddit Ads Tools
|
|
175
|
+
|
|
176
|
+
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`).
|
|
177
|
+
|
|
178
|
+
| Tool | What It Does |
|
|
179
|
+
|------|-------------|
|
|
180
|
+
| `list_reddit_accounts` | Discover businesses and ad accounts (currency, time zone, approval state) |
|
|
181
|
+
| `list_reddit_funding_instruments` | Billing instruments and posting profiles — prerequisites for creating campaigns and ads |
|
|
182
|
+
| `get_reddit_campaigns` / `get_reddit_ad_groups` / `get_reddit_ads` | Structure with configured vs effective status, budgets, bids, pixel, targeting, weekly schedule, rejection reasons; `include_copy` returns each ad's post (headline, body, destination, media) |
|
|
183
|
+
| `get_reddit_performance` | Spend, clicks, CTR, CPC, conversions, CPA, ROAS per account/campaign/ad group/ad, optional breakdown (date, country, community, keyword, placement, …), compact mode with insights |
|
|
184
|
+
| `run_reddit_report` | Raw reports endpoint for any metric (REACH, video, per-event conversions) |
|
|
185
|
+
| `get_reddit_pixels` | Pixels and when each event last fired — flags ad groups optimizing for events the pixel never sent |
|
|
186
|
+
| `search_reddit_targeting` | Communities, interests, geolocations, languages, keyword suggestions, and Reddit's related-community suggestions (by seed communities or website) |
|
|
187
|
+
| `get_reddit_account_history` | Who changed what and when: field, before/after, member |
|
|
188
|
+
| `estimate_reddit_ad_group` | Audience size, delivery estimate and suggested bid range for a planned ad group (Reddit's counterpart of `estimate_budget`) |
|
|
189
|
+
| `pause_reddit_entity` / `enable_reddit_entity` / `remove_reddit_entity` | Status changes through the preview gate (remove = ARCHIVE, irreversible, double-confirmed) |
|
|
190
|
+
| `update_reddit_campaign` / `update_reddit_ad_group` / `update_reddit_ad` | Budget, 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 |
|
|
191
|
+
| `draft_reddit_campaign` / `draft_reddit_ad_group` / `draft_reddit_ad` | Create campaign → ad group (pixel + targeting required) → post + ad, or promote an existing post with `post_id`. Everything is created **PAUSED**. |
|
|
192
|
+
|
|
193
|
+
> **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.
|
|
194
|
+
|
|
200
195
|
### Planning Tools
|
|
201
196
|
|
|
202
197
|
| Tool | What It Does |
|
|
@@ -277,7 +272,7 @@ AdLoop manages real ad spend, so safety is not optional.
|
|
|
277
272
|
|
|
278
273
|
> **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.
|
|
279
274
|
>
|
|
280
|
-
> **Prefer zero setup?** [**AdLoop Cloud**](https://getadloop.com) is the hosted version: connect Google in two clicks — no Cloud project, no
|
|
275
|
+
> **Prefer zero setup?** [**AdLoop Cloud**](https://getadloop.com) is the hosted version: connect Google in two clicks — no Cloud project, no API access application, EU-hosted.
|
|
281
276
|
>
|
|
282
277
|
> *(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.)*
|
|
283
278
|
|
|
@@ -307,8 +302,8 @@ uv run adloop init
|
|
|
307
302
|
The wizard walks you through:
|
|
308
303
|
|
|
309
304
|
1. **Google Cloud setup** — creates a project, enables the three APIs, generates an OAuth client (see [Custom Google Cloud Project Setup](#custom-google-cloud-project-setup) below for the exact steps the wizard refers you to)
|
|
310
|
-
2. **
|
|
311
|
-
3. **MCC Account ID** —
|
|
305
|
+
2. **Google Ads API access** — applied for on your Cloud project's [Google Ads API Overview page](https://console.cloud.google.com/google/ads-apis/overview); the legacy developer-token prompt can be left empty
|
|
306
|
+
3. **MCC Account ID** — optional, only if you reach several accounts through a Manager Account
|
|
312
307
|
4. **OAuth sign-in** — opens a browser to sign in with Google (or prints a URL for headless servers)
|
|
313
308
|
5. **Auto-discovers your accounts** — finds your GA4 properties and Ads accounts automatically
|
|
314
309
|
6. **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
|
|
@@ -319,26 +314,25 @@ The wizard walks you through:
|
|
|
319
314
|
### Requirements
|
|
320
315
|
|
|
321
316
|
- Python 3.11+
|
|
322
|
-
- A Google Ads account
|
|
323
|
-
-
|
|
317
|
+
- A Google Ads account (a Manager Account is optional; you only need one to reach several accounts through one login)
|
|
318
|
+
- Google Ads API access on your Google Cloud project (see below)
|
|
324
319
|
|
|
325
|
-
### Google Ads
|
|
320
|
+
### Google Ads API access
|
|
326
321
|
|
|
327
|
-
|
|
322
|
+
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.
|
|
328
323
|
|
|
329
|
-
|
|
330
|
-
2. In the MCC, go to **Tools & Settings → API Center**
|
|
331
|
-
3. Your **developer token** is shown there. Copy it — the wizard will ask for it.
|
|
324
|
+
Apply on your project's [Google Ads API Overview page](https://console.cloud.google.com/google/ads-apis/overview) in the Cloud Console:
|
|
332
325
|
|
|
333
|
-
|
|
326
|
+
| Level | How to get it | What it allows |
|
|
327
|
+
|-------|---------------|----------------|
|
|
328
|
+
| **Test** | Automatic when you enable the Google Ads API | Test accounts only, **not production accounts**. `CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION` (or `DEVELOPER_TOKEN_NOT_APPROVED` on older API versions) means you are here. |
|
|
329
|
+
| **Explorer** | "Upgrade access level" → apply; usually granted automatically | 2,880 operations/day on production accounts. Enough to get started. |
|
|
330
|
+
| **Basic** | Apply; reviewed automatically within minutes once the project is brand-verified (OAuth consent screen set to External and published "In production") | 15,000 operations/day. |
|
|
331
|
+
| **Standard** | Apply; manual review, about ten business days | Higher limits; Google's RMF policy applies |
|
|
334
332
|
|
|
335
|
-
|
|
336
|
-
|-------|-----------|----------------|
|
|
337
|
-
| **Test Account** | Default for new tokens | Can only access test accounts — **not production accounts**. If you see `DEVELOPER_TOKEN_NOT_APPROVED`, this is why. |
|
|
338
|
-
| **Explorer** | Automatic after first API call with a production account | 2,880 operations/day on production accounts. Enough to get started. |
|
|
339
|
-
| **Basic** | [Apply through API Center](https://ads.google.com/aw/apicenter) | 15,000 operations/day. Apply if you need more. |
|
|
333
|
+
Projects on a Cloud free trial or with billing disabled are refused Explorer and Basic; use a project with billing set up.
|
|
340
334
|
|
|
341
|
-
> **Getting `
|
|
335
|
+
> **Getting `CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION`?** Your project is at Test level. Open the Overview page above and apply for Explorer access.
|
|
342
336
|
|
|
343
337
|
### Headless Servers
|
|
344
338
|
|
|
@@ -443,9 +437,13 @@ All configuration lives in `~/.adloop/config.yaml`. See [`config.yaml.example`](
|
|
|
443
437
|
| `google` | `credentials_path` | *(empty)* | Path to OAuth client JSON or service account key. Empty = `~/.adloop/credentials.json`, else Application Default Credentials. |
|
|
444
438
|
| `google` | `token_path` | `~/.adloop/token.json` | Where to store the OAuth token (auto-created) |
|
|
445
439
|
| `ga4` | `property_id` | — | Your GA4 property ID (auto-discovered by `adloop init`) |
|
|
446
|
-
| `ads` | `developer_token` | — |
|
|
440
|
+
| `ads` | `developer_token` | — | Legacy, optional: API access belongs to your Cloud project since Sept 2026 |
|
|
447
441
|
| `ads` | `customer_id` | — | Default Google Ads customer ID (auto-discovered by `adloop init`) |
|
|
448
442
|
| `ads` | `login_customer_id` | — | Your MCC account ID |
|
|
443
|
+
| `reddit` | `client_id` / `client_secret` | *(empty)* | Your Reddit developer app (Business Manager → Developer Application) |
|
|
444
|
+
| `reddit` | `ad_account_id` | *(empty)* | Default Reddit ad account for every Reddit tool (picked by `adloop init`) |
|
|
445
|
+
| `reddit` | `username` | *(empty)* | Your Reddit username, used only in the User-Agent Reddit requires |
|
|
446
|
+
| `reddit` | `token_path` | `~/.adloop/reddit_token.json` | Where the Reddit refresh token is stored |
|
|
449
447
|
| `safety` | `max_daily_budget` | `50.00` | Maximum allowed daily budget per campaign |
|
|
450
448
|
| `safety` | `require_dry_run` | `true` | Force all writes to dry-run mode |
|
|
451
449
|
| `safety` | `two_phase_apply` | `false` | Refuse real applies until the plan had a dry-run pass |
|
|
@@ -468,6 +466,7 @@ Most MCP clients (claude.ai, ChatGPT, Cursor, …) load **every tool schema into
|
|
|
468
466
|
| `gsc` | Search Console reads |
|
|
469
467
|
| `web` | PageSpeed / Core Web Vitals |
|
|
470
468
|
| `merchant` | Merchant Center feed health |
|
|
469
|
+
| `reddit` | Reddit Ads reads, writes, and planning |
|
|
471
470
|
|
|
472
471
|
`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.
|
|
473
472
|
|
|
@@ -478,7 +477,7 @@ On [AdLoop Cloud](https://getadloop.com), the same feature is per API key: pick
|
|
|
478
477
|
```
|
|
479
478
|
src/adloop/
|
|
480
479
|
├── __init__.py # Entry point — routes 'adloop init' to wizard, otherwise starts MCP server
|
|
481
|
-
├── server.py # FastMCP server —
|
|
480
|
+
├── server.py # FastMCP server — every tool registration with safety annotations and toolset tags
|
|
482
481
|
├── config.py # Config loader (~/.adloop/config.yaml)
|
|
483
482
|
├── auth.py # OAuth 2.0 flow (user-supplied credentials, headless fallback) + service accounts; GA4 / Ads / GTM scopes
|
|
484
483
|
├── cli.py # Interactive 'adloop init' setup wizard
|
|
@@ -498,6 +497,11 @@ src/adloop/
|
|
|
498
497
|
├── gtm/
|
|
499
498
|
│ ├── client.py # Google Tag Manager API v2 client
|
|
500
499
|
│ └── read.py # Live container fetching, tag/trigger/variable parsing, workspace diff, version history
|
|
500
|
+
├── reddit/
|
|
501
|
+
│ ├── auth.py # Reddit OAuth2 (own app, permanent refresh token, loopback flow for adloop init)
|
|
502
|
+
│ ├── client.py # Reddit Ads API v3 REST client — bearer refresh, per-user rate limits, pagination
|
|
503
|
+
│ ├── read.py # Accounts, campaigns, ad groups, ads, performance reports, pixels, targeting lookups
|
|
504
|
+
│ └── write.py # Draft/preflight/apply for status, budget, bid, targeting, and PAUSED creation
|
|
501
505
|
└── safety/
|
|
502
506
|
├── guards.py # Budget caps, bid limits, blocked operations, Broad Match safety
|
|
503
507
|
├── preview.py # Change plans and previews
|
|
@@ -519,7 +523,7 @@ What's been shipped and what's next:
|
|
|
519
523
|
- ~~Claude Code support~~ ✓ — `CLAUDE.md`, `.mcp.json`, `.claude/rules/`, `.claude/commands/`, CLI wizard snippets
|
|
520
524
|
- **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
|
|
521
525
|
- ~~PyPI package~~ ✓ — `pip install adloop`
|
|
522
|
-
- ~~[AdLoop Cloud](https://getadloop.com)~~ ✓ — the hosted version: no Google Cloud project, no
|
|
526
|
+
- ~~[AdLoop Cloud](https://getadloop.com)~~ ✓ — the hosted version: no Google Cloud project, no API access application, connect Google in two clicks (EU-hosted, GDPR-first)
|
|
523
527
|
- ~~Headless server support~~ ✓ — manual URL copy-paste flow for servers without a browser
|
|
524
528
|
- ~~Behavioral eval suites~~ ✓ — 28 prompt-and-expectation tests covering read, write, tracking, and planning workflows
|
|
525
529
|
- ~~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
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "adloop"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "The AI command center for Google Ads, GA4, and tracking code."
|
|
3
|
+
version = "0.15.0"
|
|
4
|
+
description = "The AI command center for Google Ads, Reddit Ads, GA4, and tracking code."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
keywords = [
|
|
8
8
|
"mcp",
|
|
9
9
|
"google-ads",
|
|
10
|
+
"reddit-ads",
|
|
10
11
|
"google-analytics",
|
|
11
12
|
"ga4",
|
|
12
13
|
"cursor",
|
|
@@ -14,13 +15,14 @@ keywords = [
|
|
|
14
15
|
]
|
|
15
16
|
dependencies = [
|
|
16
17
|
"fastmcp>=3.0.0",
|
|
17
|
-
"google-ads>=
|
|
18
|
+
"google-ads>=32.0.0",
|
|
18
19
|
"google-analytics-data>=0.20.0",
|
|
19
20
|
"google-analytics-admin>=0.27.0",
|
|
20
21
|
"google-api-python-client>=2.100.0",
|
|
21
22
|
"google-auth-oauthlib>=1.0.0",
|
|
22
23
|
"google-api-python-client>=2.0.0",
|
|
23
24
|
"pyyaml>=6.0",
|
|
25
|
+
"requests>=2.31",
|
|
24
26
|
]
|
|
25
27
|
|
|
26
28
|
[[project.authors]]
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "adloop"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "The AI command center for Google Ads, GA4, and tracking code."
|
|
3
|
+
version = "0.15.0"
|
|
4
|
+
description = "The AI command center for Google Ads, Reddit Ads, GA4, and tracking code."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
7
7
|
{ name = "Daniel Klose", email = "info@daniel-klose.com" }
|
|
8
8
|
]
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
license = { text = "MIT" }
|
|
11
|
-
keywords = ["mcp", "google-ads", "google-analytics", "ga4", "cursor", "marketing"]
|
|
11
|
+
keywords = ["mcp", "google-ads", "reddit-ads", "google-analytics", "ga4", "cursor", "marketing"]
|
|
12
12
|
dependencies = [
|
|
13
13
|
"fastmcp>=3.0.0",
|
|
14
|
-
"google-ads>=
|
|
14
|
+
"google-ads>=32.0.0",
|
|
15
15
|
"google-analytics-data>=0.20.0",
|
|
16
16
|
"google-analytics-admin>=0.27.0",
|
|
17
17
|
"google-api-python-client>=2.100.0",
|
|
18
18
|
"google-auth-oauthlib>=1.0.0",
|
|
19
19
|
"google-api-python-client>=2.0.0",
|
|
20
20
|
"pyyaml>=6.0",
|
|
21
|
+
# Reddit Ads has no SDK; the REST client (adloop.reddit.client) and the
|
|
22
|
+
# PageSpeed helper call requests directly.
|
|
23
|
+
"requests>=2.31",
|
|
21
24
|
]
|
|
22
25
|
|
|
23
26
|
[project.urls]
|
|
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
|
|
16
16
|
# Pin the API version so library upgrades don't silently break field names,
|
|
17
17
|
# enum values, or mutate operation structures. Bump this deliberately when
|
|
18
18
|
# migrating to a new API version — never let it float to the library default.
|
|
19
|
-
GOOGLE_ADS_API_VERSION = "
|
|
19
|
+
GOOGLE_ADS_API_VERSION = "v25"
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def get_ads_client(config: AdLoopConfig) -> GoogleAdsClient:
|
|
@@ -28,11 +28,16 @@ def get_ads_client(config: AdLoopConfig) -> GoogleAdsClient:
|
|
|
28
28
|
credentials = get_ads_credentials(config)
|
|
29
29
|
|
|
30
30
|
client_config = {
|
|
31
|
-
"developer_token": config.ads.developer_token,
|
|
32
31
|
"use_proto_plus": True,
|
|
33
32
|
"version": GOOGLE_ADS_API_VERSION,
|
|
34
33
|
}
|
|
35
34
|
|
|
35
|
+
# Access levels live on the Google Cloud project that owns the OAuth
|
|
36
|
+
# client since 2026-09-09; the developer token header is optional and
|
|
37
|
+
# ignored by the API. Send it only when someone still has one configured.
|
|
38
|
+
if config.ads.developer_token:
|
|
39
|
+
client_config["developer_token"] = config.ads.developer_token
|
|
40
|
+
|
|
36
41
|
if config.ads.login_customer_id:
|
|
37
42
|
client_config["login_customer_id"] = config.ads.login_customer_id.replace("-", "")
|
|
38
43
|
|