adloop 0.14.0__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.
Files changed (59) hide show
  1. {adloop-0.14.0 → adloop-0.15.0}/PKG-INFO +23 -24
  2. {adloop-0.14.0 → adloop-0.15.0}/README.md +21 -22
  3. {adloop-0.14.0 → adloop-0.15.0}/pyproject.toml +2 -2
  4. {adloop-0.14.0 → adloop-0.15.0}/pyproject.toml.orig +2 -2
  5. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ads/client.py +7 -2
  6. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ads/enums.py +2 -4
  7. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ads/forecast.py +4 -5
  8. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ads/gaql.py +15 -6
  9. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/cli.py +26 -13
  10. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/server.py +20 -10
  11. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/__init__.py +0 -0
  12. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/__main__.py +0 -0
  13. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/_mcp_patches.py +0 -0
  14. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ads/__init__.py +0 -0
  15. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ads/conversion_actions.py +0 -0
  16. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ads/currency.py +0 -0
  17. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ads/pmax.py +0 -0
  18. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ads/read.py +0 -0
  19. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ads/write.py +0 -0
  20. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/auth.py +0 -0
  21. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/config.py +0 -0
  22. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/crossref.py +0 -0
  23. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/diagnostics.py +0 -0
  24. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ga4/__init__.py +0 -0
  25. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ga4/client.py +0 -0
  26. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ga4/reports.py +0 -0
  27. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ga4/tracking.py +0 -0
  28. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/ga4/write.py +0 -0
  29. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/gsc/__init__.py +0 -0
  30. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/gsc/client.py +0 -0
  31. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/gsc/reports.py +0 -0
  32. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/gtm/__init__.py +0 -0
  33. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/gtm/client.py +0 -0
  34. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/gtm/read.py +0 -0
  35. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/merchant/__init__.py +0 -0
  36. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/merchant/client.py +0 -0
  37. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/merchant/read.py +0 -0
  38. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/pagespeed.py +0 -0
  39. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/reddit/__init__.py +0 -0
  40. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/reddit/auth.py +0 -0
  41. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/reddit/client.py +0 -0
  42. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/reddit/read.py +0 -0
  43. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/reddit/schedule.py +0 -0
  44. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/reddit/write.py +0 -0
  45. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/rules/__init__.py +0 -0
  46. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/rules/adloop.md +0 -0
  47. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/rules/commands/analyze-performance.md +0 -0
  48. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/rules/commands/budget-plan.md +0 -0
  49. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/rules/commands/create-ad.md +0 -0
  50. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/rules/commands/create-campaign.md +0 -0
  51. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/rules/commands/diagnose-tracking.md +0 -0
  52. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/rules/commands/optimize-campaign.md +0 -0
  53. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/rules_install.py +0 -0
  54. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/runtime.py +0 -0
  55. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/safety/__init__.py +0 -0
  56. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/safety/audit.py +0 -0
  57. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/safety/guards.py +0 -0
  58. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/safety/preview.py +0 -0
  59. {adloop-0.14.0 → adloop-0.15.0}/src/adloop/tracking.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: adloop
3
- Version: 0.14.0
3
+ Version: 0.15.0
4
4
  Summary: The AI command center for Google Ads, Reddit Ads, GA4, and tracking code.
5
5
  Keywords: mcp,google-ads,reddit-ads,google-analytics,ga4,cursor,marketing
6
6
  Author: Daniel Klose
7
7
  Author-email: Daniel Klose <info@daniel-klose.com>
8
8
  License: MIT
9
9
  Requires-Dist: fastmcp>=3.0.0
10
- Requires-Dist: google-ads>=31.1.0
10
+ Requires-Dist: google-ads>=32.0.0
11
11
  Requires-Dist: google-analytics-data>=0.20.0
12
12
  Requires-Dist: google-analytics-admin>=0.27.0
13
13
  Requires-Dist: google-api-python-client>=2.100.0
@@ -37,7 +37,7 @@ Description-Content-Type: text/markdown
37
37
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
38
38
  [![Python 3.11+](https://img.shields.io/badge/Python-3.11+-3776AB.svg?logo=python&logoColor=white)](https://www.python.org/downloads/)
39
39
  [![MCP Compatible](https://img.shields.io/badge/MCP-Compatible-8A2BE2.svg)](https://modelcontextprotocol.io)
40
- [![Google Ads API](https://img.shields.io/badge/Google%20Ads-API%20v24-4285F4.svg?logo=google-ads&logoColor=white)](https://developers.google.com/google-ads/api/docs/start)
40
+ [![Google Ads API](https://img.shields.io/badge/Google%20Ads-API%20v25-4285F4.svg?logo=google-ads&logoColor=white)](https://developers.google.com/google-ads/api/docs/start)
41
41
  [![GA4 Data API](https://img.shields.io/badge/GA4-Data%20API-E37400.svg?logo=google-analytics&logoColor=white)](https://developers.google.com/analytics/devguides/reporting/data/v1)
42
42
  [![GitHub stars](https://img.shields.io/github/stars/kLOsk/adloop?style=social)](https://github.com/kLOsk/adloop)
43
43
 
@@ -48,7 +48,7 @@ An MCP server that gives your AI assistant read + write access to Google Ads, Re
48
48
  </div>
49
49
 
50
50
  > [!TIP]
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 developer token, no OAuth verification wait. EU-hosted, GDPR-first, DPA included.
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.
52
52
 
53
53
  > 📚 **Documentation: [docs.getadloop.com](https://docs.getadloop.com)** — setup guides per AI client, toolsets, the safety model, and troubleshooting for both editions.
54
54
 
@@ -62,7 +62,7 @@ Both versions run the same tools with the same safety model. The difference is w
62
62
  |---|---|---|
63
63
  | **Setup** | Connect Google in two clicks | ~5 min: own Google Cloud project + `adloop init` |
64
64
  | **Google Cloud project** | Not needed | Required (free) |
65
- | **Ads developer token** | Not needed | Required (from your MCC) |
65
+ | **Ads API access** | Not needed | Granted to your Cloud project (no developer token, no MCC since Sept 2026) |
66
66
  | **Works with** | claude.ai, ChatGPT, Claude Code, Cursor, Gemini | Claude Code, Cursor, Claude Desktop, any local MCP client |
67
67
  | **Where your data flows** | EU servers (Germany), GDPR-first, DPA included | 100% your machine — nothing leaves it |
68
68
  | **Updates** | Automatic | `pip install -U adloop` |
@@ -299,7 +299,7 @@ AdLoop manages real ad spend, so safety is not optional.
299
299
 
300
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.
301
301
  >
302
- > **Prefer zero setup?** [**AdLoop Cloud**](https://getadloop.com) is the hosted version: connect Google in two clicks — no Cloud project, no developer token, EU-hosted.
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.
303
303
  >
304
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.)*
305
305
 
@@ -329,8 +329,8 @@ uv run adloop init
329
329
  The wizard walks you through:
330
330
 
331
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)
332
- 2. **Developer token** — from your Google Ads MCC ([API Center](https://ads.google.com/aw/apicenter))
333
- 3. **MCC Account ID** — your Manager Account ID (top bar in the MCC UI)
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
334
334
  4. **OAuth sign-in** — opens a browser to sign in with Google (or prints a URL for headless servers)
335
335
  5. **Auto-discovers your accounts** — finds your GA4 properties and Ads accounts automatically
336
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
@@ -341,26 +341,25 @@ The wizard walks you through:
341
341
  ### Requirements
342
342
 
343
343
  - Python 3.11+
344
- - A Google Ads account with an MCC (Manager Account)
345
- - A Google Ads Developer Token (see below)
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)
346
346
 
347
- ### Google Ads Developer Token
347
+ ### Google Ads API access
348
348
 
349
- A developer token is **always required**. Your OAuth client handles Google sign-in; the developer token is a separate key that grants API access to your Google Ads data.
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.
350
350
 
351
- 1. **Create an MCC** (free) at [ads.google.com/home/tools/manager-accounts](https://ads.google.com/home/tools/manager-accounts/) if you don't have one. Link your regular Google Ads account to it.
352
- 2. In the MCC, go to **Tools & Settings → API Center**
353
- 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:
354
352
 
355
- **Access levels** — your token's access level determines what it can do:
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 |
356
359
 
357
- | Level | How to Get | What It Allows |
358
- |-------|-----------|----------------|
359
- | **Test Account** | Default for new tokens | Can only access test accounts — **not production accounts**. If you see `DEVELOPER_TOKEN_NOT_APPROVED`, this is why. |
360
- | **Explorer** | Automatic after first API call with a production account | 2,880 operations/day on production accounts. Enough to get started. |
361
- | **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.
362
361
 
363
- > **Getting `DEVELOPER_TOKEN_NOT_APPROVED`?** Your token is at "Test Account" level. Go to [API Center](https://ads.google.com/aw/apicenter) in your MCC and check your access level. If it shows "Test Account", you need to apply for Basic access or wait for Explorer access to be granted after your first production API call.
362
+ > **Getting `CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION`?** Your project is at Test level. Open the Overview page above and apply for Explorer access.
364
363
 
365
364
  ### Headless Servers
366
365
 
@@ -465,7 +464,7 @@ All configuration lives in `~/.adloop/config.yaml`. See [`config.yaml.example`](
465
464
  | `google` | `credentials_path` | *(empty)* | Path to OAuth client JSON or service account key. Empty = `~/.adloop/credentials.json`, else Application Default Credentials. |
466
465
  | `google` | `token_path` | `~/.adloop/token.json` | Where to store the OAuth token (auto-created) |
467
466
  | `ga4` | `property_id` | — | Your GA4 property ID (auto-discovered by `adloop init`) |
468
- | `ads` | `developer_token` | — | Your Google Ads API developer token |
467
+ | `ads` | `developer_token` | — | Legacy, optional: API access belongs to your Cloud project since Sept 2026 |
469
468
  | `ads` | `customer_id` | — | Default Google Ads customer ID (auto-discovered by `adloop init`) |
470
469
  | `ads` | `login_customer_id` | — | Your MCC account ID |
471
470
  | `reddit` | `client_id` / `client_secret` | *(empty)* | Your Reddit developer app (Business Manager → Developer Application) |
@@ -551,7 +550,7 @@ What's been shipped and what's next:
551
550
  - ~~Claude Code support~~ ✓ — `CLAUDE.md`, `.mcp.json`, `.claude/rules/`, `.claude/commands/`, CLI wizard snippets
552
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
553
552
  - ~~PyPI package~~ ✓ — `pip install adloop`
554
- - ~~[AdLoop Cloud](https://getadloop.com)~~ ✓ — the hosted version: no Google Cloud project, no developer token, connect Google in two clicks (EU-hosted, GDPR-first)
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)
555
554
  - ~~Headless server support~~ ✓ — manual URL copy-paste flow for servers without a browser
556
555
  - ~~Behavioral eval suites~~ ✓ — 28 prompt-and-expectation tests covering read, write, tracking, and planning workflows
557
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
@@ -10,7 +10,7 @@
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
11
11
  [![Python 3.11+](https://img.shields.io/badge/Python-3.11+-3776AB.svg?logo=python&logoColor=white)](https://www.python.org/downloads/)
12
12
  [![MCP Compatible](https://img.shields.io/badge/MCP-Compatible-8A2BE2.svg)](https://modelcontextprotocol.io)
13
- [![Google Ads API](https://img.shields.io/badge/Google%20Ads-API%20v24-4285F4.svg?logo=google-ads&logoColor=white)](https://developers.google.com/google-ads/api/docs/start)
13
+ [![Google Ads API](https://img.shields.io/badge/Google%20Ads-API%20v25-4285F4.svg?logo=google-ads&logoColor=white)](https://developers.google.com/google-ads/api/docs/start)
14
14
  [![GA4 Data API](https://img.shields.io/badge/GA4-Data%20API-E37400.svg?logo=google-analytics&logoColor=white)](https://developers.google.com/analytics/devguides/reporting/data/v1)
15
15
  [![GitHub stars](https://img.shields.io/github/stars/kLOsk/adloop?style=social)](https://github.com/kLOsk/adloop)
16
16
 
@@ -21,7 +21,7 @@ An MCP server that gives your AI assistant read + write access to Google Ads, Re
21
21
  </div>
22
22
 
23
23
  > [!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 developer token, no OAuth verification wait. EU-hosted, GDPR-first, DPA included.
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.
25
25
 
26
26
  > 📚 **Documentation: [docs.getadloop.com](https://docs.getadloop.com)** — setup guides per AI client, toolsets, the safety model, and troubleshooting for both editions.
27
27
 
@@ -35,7 +35,7 @@ Both versions run the same tools with the same safety model. The difference is w
35
35
  |---|---|---|
36
36
  | **Setup** | Connect Google in two clicks | ~5 min: own Google Cloud project + `adloop init` |
37
37
  | **Google Cloud project** | Not needed | Required (free) |
38
- | **Ads developer token** | Not needed | Required (from your MCC) |
38
+ | **Ads API access** | Not needed | Granted to your Cloud project (no developer token, no MCC since Sept 2026) |
39
39
  | **Works with** | claude.ai, ChatGPT, Claude Code, Cursor, Gemini | Claude Code, Cursor, Claude Desktop, any local MCP client |
40
40
  | **Where your data flows** | EU servers (Germany), GDPR-first, DPA included | 100% your machine — nothing leaves it |
41
41
  | **Updates** | Automatic | `pip install -U adloop` |
@@ -272,7 +272,7 @@ AdLoop manages real ad spend, so safety is not optional.
272
272
 
273
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.
274
274
  >
275
- > **Prefer zero setup?** [**AdLoop Cloud**](https://getadloop.com) is the hosted version: connect Google in two clicks — no Cloud project, no developer token, EU-hosted.
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.
276
276
  >
277
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.)*
278
278
 
@@ -302,8 +302,8 @@ uv run adloop init
302
302
  The wizard walks you through:
303
303
 
304
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)
305
- 2. **Developer token** — from your Google Ads MCC ([API Center](https://ads.google.com/aw/apicenter))
306
- 3. **MCC Account ID** — your Manager Account ID (top bar in the MCC UI)
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
307
307
  4. **OAuth sign-in** — opens a browser to sign in with Google (or prints a URL for headless servers)
308
308
  5. **Auto-discovers your accounts** — finds your GA4 properties and Ads accounts automatically
309
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
@@ -314,26 +314,25 @@ The wizard walks you through:
314
314
  ### Requirements
315
315
 
316
316
  - Python 3.11+
317
- - A Google Ads account with an MCC (Manager Account)
318
- - A Google Ads Developer Token (see below)
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)
319
319
 
320
- ### Google Ads Developer Token
320
+ ### Google Ads API access
321
321
 
322
- A developer token is **always required**. Your OAuth client handles Google sign-in; the developer token is a separate key that grants API access to your Google Ads data.
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.
323
323
 
324
- 1. **Create an MCC** (free) at [ads.google.com/home/tools/manager-accounts](https://ads.google.com/home/tools/manager-accounts/) if you don't have one. Link your regular Google Ads account to it.
325
- 2. In the MCC, go to **Tools & Settings → API Center**
326
- 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:
327
325
 
328
- **Access levels** — your token's access level determines what it can do:
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 |
329
332
 
330
- | Level | How to Get | What It Allows |
331
- |-------|-----------|----------------|
332
- | **Test Account** | Default for new tokens | Can only access test accounts — **not production accounts**. If you see `DEVELOPER_TOKEN_NOT_APPROVED`, this is why. |
333
- | **Explorer** | Automatic after first API call with a production account | 2,880 operations/day on production accounts. Enough to get started. |
334
- | **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.
335
334
 
336
- > **Getting `DEVELOPER_TOKEN_NOT_APPROVED`?** Your token is at "Test Account" level. Go to [API Center](https://ads.google.com/aw/apicenter) in your MCC and check your access level. If it shows "Test Account", you need to apply for Basic access or wait for Explorer access to be granted after your first production API call.
335
+ > **Getting `CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION`?** Your project is at Test level. Open the Overview page above and apply for Explorer access.
337
336
 
338
337
  ### Headless Servers
339
338
 
@@ -438,7 +437,7 @@ All configuration lives in `~/.adloop/config.yaml`. See [`config.yaml.example`](
438
437
  | `google` | `credentials_path` | *(empty)* | Path to OAuth client JSON or service account key. Empty = `~/.adloop/credentials.json`, else Application Default Credentials. |
439
438
  | `google` | `token_path` | `~/.adloop/token.json` | Where to store the OAuth token (auto-created) |
440
439
  | `ga4` | `property_id` | — | Your GA4 property ID (auto-discovered by `adloop init`) |
441
- | `ads` | `developer_token` | — | Your Google Ads API developer token |
440
+ | `ads` | `developer_token` | — | Legacy, optional: API access belongs to your Cloud project since Sept 2026 |
442
441
  | `ads` | `customer_id` | — | Default Google Ads customer ID (auto-discovered by `adloop init`) |
443
442
  | `ads` | `login_customer_id` | — | Your MCC account ID |
444
443
  | `reddit` | `client_id` / `client_secret` | *(empty)* | Your Reddit developer app (Business Manager → Developer Application) |
@@ -524,7 +523,7 @@ What's been shipped and what's next:
524
523
  - ~~Claude Code support~~ ✓ — `CLAUDE.md`, `.mcp.json`, `.claude/rules/`, `.claude/commands/`, CLI wizard snippets
525
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
526
525
  - ~~PyPI package~~ ✓ — `pip install adloop`
527
- - ~~[AdLoop Cloud](https://getadloop.com)~~ ✓ — the hosted version: no Google Cloud project, no developer token, connect Google in two clicks (EU-hosted, GDPR-first)
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)
528
527
  - ~~Headless server support~~ ✓ — manual URL copy-paste flow for servers without a browser
529
528
  - ~~Behavioral eval suites~~ ✓ — 28 prompt-and-expectation tests covering read, write, tracking, and planning workflows
530
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,6 +1,6 @@
1
1
  [project]
2
2
  name = "adloop"
3
- version = "0.14.0"
3
+ version = "0.15.0"
4
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"
@@ -15,7 +15,7 @@ keywords = [
15
15
  ]
16
16
  dependencies = [
17
17
  "fastmcp>=3.0.0",
18
- "google-ads>=31.1.0",
18
+ "google-ads>=32.0.0",
19
19
  "google-analytics-data>=0.20.0",
20
20
  "google-analytics-admin>=0.27.0",
21
21
  "google-api-python-client>=2.100.0",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "adloop"
3
- version = "0.14.0"
3
+ version = "0.15.0"
4
4
  description = "The AI command center for Google Ads, Reddit Ads, GA4, and tracking code."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -11,7 +11,7 @@ license = { text = "MIT" }
11
11
  keywords = ["mcp", "google-ads", "reddit-ads", "google-analytics", "ga4", "cursor", "marketing"]
12
12
  dependencies = [
13
13
  "fastmcp>=3.0.0",
14
- "google-ads>=31.1.0",
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",
@@ -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 = "v24"
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
 
@@ -22,9 +22,8 @@ def _enum_introspection_client():
22
22
  """Memoized no-auth GoogleAdsClient used purely for enum introspection.
23
23
 
24
24
  The client constructor doesn't make any network calls and doesn't
25
- validate credentials beyond requiring SOMETHING in the developer-token
26
- field — perfect for reading the bundled enum protos. We cache it so
27
- every enum_names() call after the first is essentially free.
25
+ validate credentials — perfect for reading the bundled enum protos. We
26
+ cache it so every enum_names() call after the first is essentially free.
28
27
  """
29
28
  from google.ads.googleads.client import GoogleAdsClient
30
29
 
@@ -32,7 +31,6 @@ def _enum_introspection_client():
32
31
 
33
32
  return GoogleAdsClient(
34
33
  credentials=None,
35
- developer_token="adloop-enum-introspection-not-used",
36
34
  use_proto_plus=True,
37
35
  version=GOOGLE_ADS_API_VERSION,
38
36
  )
@@ -226,7 +226,7 @@ def _build_keyword_ideas_rest_body(
226
226
  ) -> dict:
227
227
  """Build the JSON body for the REST generateKeywordIdeas endpoint.
228
228
 
229
- Schema follows google-ads REST v24 (camelCase). Exactly one of
229
+ Schema follows the pinned google-ads REST version (camelCase; unchanged v24 → v25). Exactly one of
230
230
  ``keywordSeed`` / ``urlSeed`` / ``keywordAndUrlSeed`` is set based on
231
231
  which inputs were provided.
232
232
  """
@@ -272,10 +272,9 @@ def _post_keyword_ideas_rest_page(
272
272
  credentials = get_ads_credentials(config)
273
273
  session = AuthorizedSession(credentials)
274
274
 
275
- headers = {
276
- "developer-token": config.ads.developer_token,
277
- "Content-Type": "application/json",
278
- }
275
+ headers = {"Content-Type": "application/json"}
276
+ if config.ads.developer_token:
277
+ headers["developer-token"] = config.ads.developer_token
279
278
  if config.ads.login_customer_id:
280
279
  headers["login-customer-id"] = config.ads.login_customer_id.replace("-", "")
281
280
 
@@ -63,15 +63,24 @@ def run_gaql(
63
63
  # ---------------------------------------------------------------------------
64
64
 
65
65
  _GAQL_ERROR_HINTS = {
66
+ "CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION": (
67
+ "The Google Cloud project that owns your OAuth client only has Test "
68
+ "access, which cannot reach production Google Ads accounts. Apply for "
69
+ "Explorer or Basic access on the project's Google Ads API Overview "
70
+ "page: https://console.cloud.google.com/google/ads-apis/overview"
71
+ ),
72
+ # Pre-2026-09 wording: access levels moved from developer tokens to the
73
+ # Cloud project, but older API versions still answer with these names.
66
74
  "DEVELOPER_TOKEN_NOT_APPROVED": (
67
- "Your Google Ads developer token is only approved for test accounts. "
68
- "Apply for Basic or Standard access in the Google Ads API Center, "
69
- "or use a test account."
75
+ "Your project's Google Ads API access is at Test level, which cannot "
76
+ "reach production accounts. Apply for Explorer or Basic access on the "
77
+ "Google Cloud project's Google Ads API Overview page: "
78
+ "https://console.cloud.google.com/google/ads-apis/overview"
70
79
  ),
71
80
  "DEVELOPER_TOKEN_INVALID": (
72
- "Your Google Ads developer token is invalid. Update "
73
- "`ads.developer_token` in `~/.adloop/config.yaml` using the token "
74
- "from your manager account API Center."
81
+ "The configured `ads.developer_token` is invalid. Since September 2026 "
82
+ "no token is needed at all: remove it from `~/.adloop/config.yaml`; "
83
+ "access is granted to the Google Cloud project that owns your OAuth client."
75
84
  ),
76
85
  "EXPECTED_REFERENCED_FIELD_IN_SELECT_CLAUSE": (
77
86
  "Fields used in ORDER BY or HAVING must also appear in the SELECT clause. "
@@ -33,9 +33,13 @@ _GOOGLE_CLOUD_INSTRUCTIONS = """\
33
33
  │ → https://console.cloud.google.com/apis/credentials │
34
34
  │ Download the JSON file. │
35
35
  │ │
36
- │ 5. Get your Google Ads Developer Token from your MCC account │
37
- │ → https://ads.google.com/aw/apicenter │
38
- │ (You need a Manager Account / MCC) │
36
+ │ 5. Apply for Google Ads API access ON THE PROJECT │
37
+ │ → https://console.cloud.google.com/google/ads-apis/overview │
38
+ │ New projects start at Test level (test accounts only). │
39
+ │ "Upgrade access level": Explorer is granted automatically │
40
+ │ (2,880 ops/day); Basic (15,000/day) needs the consent │
41
+ │ screen published ("In production") first. │
42
+ │ No manager account (MCC) and no developer token needed. │
39
43
  └─────────────────────────────────────────────────────────────────┘
40
44
  """
41
45
 
@@ -77,9 +81,11 @@ def _validate_customer_id(raw: str) -> str | None:
77
81
  return None
78
82
 
79
83
 
80
- def _prompt_customer_id(label: str, default: str = "") -> str:
84
+ def _prompt_customer_id(label: str, default: str = "", required: bool = True) -> str:
81
85
  while True:
82
- value = _prompt(label, default=default)
86
+ value = _prompt(label, default=default, required=required)
87
+ if not value and not required:
88
+ return ""
83
89
  formatted = _format_customer_id(value)
84
90
  err = _validate_customer_id(formatted)
85
91
  if err:
@@ -183,9 +189,10 @@ def _generate_config_yaml(
183
189
  f' property_id: "{property_id}"',
184
190
  "",
185
191
  "ads:",
192
+ " # Legacy; API access comes from the Google Cloud project since 2026-09.",
186
193
  f' developer_token: "{developer_token}"',
187
194
  f' customer_id: "{customer_id}"',
188
- " # MCC / Manager Account ID (required if using a manager account)",
195
+ " # MCC / Manager Account ID (only if using a manager account)",
189
196
  f' login_customer_id: "{login_customer_id}"',
190
197
  "",
191
198
  "gsc:",
@@ -604,23 +611,29 @@ def run_init_wizard() -> None:
604
611
  )
605
612
 
606
613
  step_num = 4
607
- _step_header(step_num, "Google Ads Developer Token")
608
- _print(" Find your developer token in your MCC account:")
609
- _print(" → https://ads.google.com/aw/apicenter")
614
+ _step_header(step_num, "Google Ads API access")
615
+ _print(" Since September 2026 API access belongs to your Google Cloud")
616
+ _print(" project, not to a developer token. Apply on the project's page:")
617
+ _print(" → https://console.cloud.google.com/google/ads-apis/overview")
618
+ _print(" Leave the token empty unless you still run an old setup.")
610
619
  _print()
611
620
  developer_token = _prompt(
612
- "Developer Token",
621
+ "Developer token (legacy, optional)",
613
622
  default=_existing("ads", "developer_token"),
623
+ required=False,
614
624
  )
615
625
 
616
- # MCC Account ID (needed before auto-discovery for Ads API calls)
626
+ # Manager account: optional since access no longer comes from an MCC.
627
+ # Still needed to reach several accounts through one login.
617
628
  step_num += 1
618
- _step_header(step_num, "MCC / Manager Account")
619
- _print(" Your MCC (Manager) account ID is in the top bar of your MCC.")
629
+ _step_header(step_num, "Manager Account (MCC), optional")
630
+ _print(" Only if you manage several accounts through a manager account:")
631
+ _print(" its ID is in the top bar of the MCC. Leave empty otherwise.")
620
632
  _print()
621
633
  login_customer_id = _prompt_customer_id(
622
634
  "MCC Account ID (XXX-XXX-XXXX)",
623
635
  default=_existing("ads", "login_customer_id"),
636
+ required=False,
624
637
  )
625
638
 
626
639
  # OAuth + auto-discovery
@@ -251,26 +251,36 @@ def _structured_error(fn_name: str, exc: Exception) -> dict:
251
251
  err = str(exc)
252
252
  err_lower = err.lower()
253
253
 
254
- if "developer_token_not_approved" in err_lower or "only approved for use with test accounts" in err_lower:
254
+ # Access levels belong to the Google Cloud project that owns the OAuth
255
+ # client (developer tokens were sunset on 2026-09-09). v25+ names the
256
+ # project; older API versions still answer with the token wording.
257
+ if (
258
+ "cloud_project_not_approved_for_production" in err_lower
259
+ or "developer_token_not_approved" in err_lower
260
+ or "only approved for use with test accounts" in err_lower
261
+ ):
255
262
  return {
256
263
  "error": (
257
- "Google Ads authorization failed — developer token is not "
258
- "approved for production accounts."
264
+ "Google Ads authorization failed — your Google Cloud project's "
265
+ "API access level (Test) cannot reach production accounts."
259
266
  ),
260
267
  "hint": (
261
- "This developer token can only access Google Ads test accounts. "
262
- "Apply for Basic or Standard access in the Google Ads API Center, "
263
- "or switch AdLoop to a test account."
268
+ "Open the project's Google Ads API Overview page "
269
+ "(https://console.cloud.google.com/google/ads-apis/overview) and "
270
+ "apply for Explorer access (usually granted automatically) or "
271
+ "Basic access (needs the OAuth consent screen brand-verified: "
272
+ "External and In production). Or switch AdLoop to a test account."
264
273
  ),
265
- "auth_error": "DEVELOPER_TOKEN_NOT_APPROVED",
274
+ "auth_error": "CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION",
266
275
  }
267
276
 
268
277
  if "developer_token_invalid" in err_lower or "developer token is not valid" in err_lower:
269
278
  return {
270
- "error": "Google Ads authentication failed — developer token is invalid.",
279
+ "error": "Google Ads authentication failed — the configured developer token is invalid.",
271
280
  "hint": (
272
- "Update `ads.developer_token` in `~/.adloop/config.yaml` with "
273
- "the token from your Google Ads manager account API Center. "
281
+ "Since September 2026 no developer token is needed: remove "
282
+ "`ads.developer_token` from `~/.adloop/config.yaml`. API access "
283
+ "belongs to the Google Cloud project that owns your OAuth client. "
274
284
  "OAuth is working if GA4 tools succeed."
275
285
  ),
276
286
  "auth_error": "DEVELOPER_TOKEN_INVALID",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes