Skip to main content
Glama
saifshabsug

Google Ads MCP Pro

by saifshabsug
README.md
<div align="center">

# Google Ads MCP Pro

**Stop reading dashboards. Start changing them.**

A Model Context Protocol server that gives your AI assistant **full read *and* write** control over Google Ads — build campaigns, fix targeting leaks, rewrite ads, and switch bidding strategies without ever opening the browser.

[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![Google Ads API](https://img.shields.io/badge/Google%20Ads%20API-v24-4285F4.svg)](https://developers.google.com/google-ads/api/docs/start)
[![MCP](https://img.shields.io/badge/MCP-compatible-purple.svg)](https://modelcontextprotocol.io)
[![Tools](https://img.shields.io/badge/tools-43-orange.svg)](#-the-43-tools)

</div>

---

## Why this exists

Most Google Ads MCP servers are **read-only**. They tell you your CPC went up. They can't lower it.

This one closes the loop:

```
"Impression share is 12%. Why?"
  → queries the API, finds 80% lost to budget

"Fix it."
  → creates a dedicated budget, relinks the campaign,
    switches to Maximize Conversions, verifies the change
```

That whole exchange happens in chat. No browser. No clicking through six settings screens.

> **Real story:** this fork was built while managing a live account. It found a campaign silently locked out of smart bidding by a shared-budget flag, a geo-targeting setting leaking spend to three foreign countries, and a sitelink contradicting the site's own lead flow — then fixed all three through the API. Read-only tooling would have surfaced none of it.

---

## What you can actually do

| Ask your assistant | What happens |
|---|---|
| *"Build a search campaign for my new service"* | Budget → campaign → ad group → keywords → RSA → sitelinks, in one pass |
| *"We're wasting money on competitor searches"* | Pulls the search-terms report, adds negative keywords |
| *"Only show my ads to people physically in Jordan"* | Flips geo targeting to `PRESENCE` (the leak most accounts never notice) |
| *"This ad's strength is Average — improve it"* | Rewrites headlines and descriptions in place |
| *"Switch to smart bidding"* | Handles the shared-budget blocker, then switches strategy |
| *"Which niches are worth entering?"* | Keyword Planner volumes, competition, and top-of-page CPC |
| *"Upload the leads that became paying clients"* | Offline conversion upload by GCLID |

Every write tool supports **`validate_only`** — a real dry run against Google's API that validates without applying. Ask for a preview before anything touches a live account.

---

## Quick start

### 1. Requirements

- Python 3.11+
- A Google Ads **developer token** ([apply here](https://developers.google.com/google-ads/api/docs/get-started/dev-token))
- OAuth desktop credentials from Google Cloud Console

### 2. Install

```bash
git clone https://github.com/saifshabsug/google-ads-mcp-pro.git
cd google-ads-mcp-pro
pip install -r requirements.txt
```

### 3. Configure

```bash
cp .env.example .env
```

```env
GOOGLE_ADS_DEVELOPER_TOKEN=your_developer_token
GOOGLE_ADS_CLIENT_ID=your_oauth_client_id
GOOGLE_ADS_CLIENT_SECRET=your_oauth_client_secret
GOOGLE_ADS_LOGIN_CUSTOMER_ID=1234567890   # your MCC, digits only
GOOGLE_ADS_CREDENTIALS_PATH=./credentials.json
GOOGLE_ADS_AUTH_TYPE=oauth
```

### 4. Register with your MCP client

<details>
<summary><b>Claude Code</b></summary>

```bash
claude mcp add google-ads -- python /full/path/to/google_ads_server.py
```
</details>

<details>
<summary><b>Claude Desktop</b> — <code>claude_desktop_config.json</code></summary>

```json
{
  "mcpServers": {
    "google-ads": {
      "command": "python",
      "args": ["/full/path/to/google_ads_server.py"],
      "env": {
        "GOOGLE_ADS_DEVELOPER_TOKEN": "...",
        "GOOGLE_ADS_CLIENT_ID": "...",
        "GOOGLE_ADS_CLIENT_SECRET": "...",
        "GOOGLE_ADS_LOGIN_CUSTOMER_ID": "...",
        "GOOGLE_ADS_CREDENTIALS_PATH": "/full/path/to/credentials.json",
        "GOOGLE_ADS_AUTH_TYPE": "oauth"
      }
    }
  }
}
```
</details>

First run opens a browser for OAuth consent and caches the token.

---

## 🧰 The 43 tools

<details open>
<summary><b>Read &amp; analyze</b> (12)</summary>

`list_accounts` · `get_account_currency` · `get_campaign_performance` · `get_ad_performance` · `get_ad_creatives` · `run_gaql` · `execute_gaql_query` · `list_resources` · `get_image_assets` · `download_image_asset` · `analyze_image_assets` · `get_asset_usage`
</details>

<details>
<summary><b>Build campaigns</b> (6)</summary>

`create_campaign_budget` · `create_search_campaign` · `create_ad_group` · `create_responsive_search_ad` · `add_keywords` · `add_location_targeting`
</details>

<details>
<summary><b>Change live settings</b> (11)</summary>

`update_campaign_status` · `update_campaign_name` · `update_campaign_budget_amount` · `update_campaign_budget_link` · `update_campaign_bidding` · `update_campaign_geo_target_type` · `update_ad_group_bid` · `update_ad_status` · `update_responsive_search_ad` · `update_keyword_status` · `set_device_bid_modifier`
</details>

<details>
<summary><b>Stop waste</b> (2)</summary>

`add_negative_keywords` · `add_ad_group_negative_keywords`
</details>

<details>
<summary><b>Ad assets</b> (6)</summary>

`add_call_asset` · `add_sitelinks` · `add_callouts` · `add_structured_snippet` · `add_ad_group_sitelinks` · `add_ad_group_callouts` · `remove_campaign_asset`
</details>

<details>
<summary><b>Conversions</b> (3)</summary>

`create_conversion_action` · `update_conversion_action` · `upload_click_conversion`
</details>

<details>
<summary><b>Planning</b> (2)</summary>

`generate_keyword_ideas` · `set_ad_schedule`
</details>

---

## Hard-won gotchas

Things the API docs bury, discovered by hitting them in production:

| Symptom | Cause | Fix |
|---|---|---|
| `BIDDING_STRATEGY_TYPE_INCOMPATIBLE_WITH_SHARED_BUDGET` | Smart bidding is rejected on any `explicitly_shared` budget — even one used by a single campaign | `create_campaign_budget` here forces `explicitly_shared: false`; use `update_campaign_budget_link` to move an existing campaign |
| `FIELD_HAS_SUBFIELDS` when switching bidding | Bidding strategies are *messages*; the field mask must name a **subfield** | Use `maximize_conversions.target_cpa_micros`, not `maximize_conversions` |
| Spend leaking to other countries | Campaigns default to `PRESENCE_OR_INTEREST` — it targets people merely *interested* in your region | `update_campaign_geo_target_type` → `PRESENCE` |
| `CANT_UPDATE_NEGATIVE` on a keyword | It's an ad-group **negative**, not a positive keyword | Remove and re-add instead |
| `LAST_3_DAYS` rejected in GAQL | Not a valid date literal | Use `BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD'` |

---

## ⚠️ Safety

This server **writes to live advertising accounts that spend real money.**

- Start with `validate_only=true` on anything you haven't run before
- Test against a low-budget or paused campaign first
- `credentials.json` and `.env` are gitignored — **keep it that way**
- Developer tokens under *Test Access* only reach test accounts; *Basic Access* is needed for production

Provided as-is under MIT. You are responsible for what runs against your account.

---

## Credits

Built on the excellent [**cohnen/mcp-google-ads**](https://github.com/cohnen/mcp-google-ads) by **Ernesto Cohnen (ixigo)**, which provided the read/reporting foundation and OAuth handling. This fork keeps all of it and adds **31 write tools**, the Google Ads API **v24** upgrade, `validate_only` dry-run support across every mutation, and the production fixes above.

MIT licensed, original copyright preserved — see [LICENSE](LICENSE).

## Contributing

Issues and PRs welcome. Especially valuable:

- New write tools (audiences, experiments, Performance Max)
- Additional gotchas for the table above
- Tests against sandbox accounts

---

<div align="center">

**Maintained by [Saif Shabsug](https://shabsug.com/elite/)** — software consultancy, Amman

*If this saved you a browser session, a ⭐ helps others find it.*

</div>

Maintenance

ActivitySlowing
ResponsivenessNo issues