LucasSantana-Dev

Google Ads MCP

Community LucasSantana-Dev
Updated

MCP server for Google Ads API — 31 tools, gated writes, exponential backoff, audit log

Google Ads MCP

Manage Google Ads campaigns through an AI assistant — safely, with every write gated behind a preview and an audit log.

CIPython 3.10+License: MIT

A Model Context Protocol server that exposes Google Ads API v24 operations as tools for Claude (or any MCP client). Read campaign metrics, search terms, and recommendations; make controlled changes to bids, budgets, statuses, and creative — all from a single conversation.

Features

31 tools across five capability layers:

Read-only reporting

Tool What it does
list_accessible_customers List all accounts under the MCC
get_campaigns List campaigns with status and channel type
get_campaign_performance Clicks, impressions, cost, conversions, CTR
get_ad_groups List ad groups with status
get_keywords Keywords with match type and status
get_search_terms Actual search queries with performance metrics
get_change_events Account change history
run_gaql_query Execute any read-only GAQL query (up to 10k rows)
get_segments_metadata Available GAQL segments
get_metrics_metadata Available GAQL metrics
get_resource_metadata Fields for any GAQL resource

Recommendations & planning

Tool What it does
get_recommendations Active Google Ads recommendations for an account or campaign
generate_keyword_ideas Keyword ideas from seed keywords or a URL
get_forecast_metrics Bid simulations and forecast data
apply_recommendation Apply a recommendation (gated)
dismiss_recommendation Dismiss a recommendation (gated)

Status writes (gated)

Tool What it does
pause_campaign / enable_campaign Toggle campaign status
pause_ad_group / enable_ad_group Toggle ad group status
pause_keyword / enable_keyword Toggle keyword status
pause_ad / enable_ad Toggle ad status

Bid & budget writes (gated + capped)

Tool What it does
update_keyword_bid Set keyword max CPC — capped at ±25% by default
update_ad_group_bid Set ad group default CPC — capped
update_campaign_budget Set daily budget — capped at ±20% by default

Entity creation (gated)

Tool What it does
create_campaign Create campaign + shared budget atomically (starts paused)
create_ad_group Create ad group under a campaign
create_keyword Add keyword to an ad group
create_ad Create a responsive search ad (starts paused)

Safety model

Every write passes through four sequential gates:

1. Allowlist  — customer_id must be in GOOGLE_ADS_MUTATE_ALLOWLIST (default: deny all)
2. Cap        — bid/budget changes exceeding the configured % are blocked outright
3. Preview    — confirm=False (default) runs validate_only; nothing changes in the account
4. Audit      — confirmed mutations are appended to an append-only JSONL log (chmod 0600)

New entities (campaigns, ad groups, keywords, ads) always start paused.

Transient quota errors (RESOURCE_TEMPORARILY_EXHAUSTED) are retried automatically with exponential back-off and jitter (configurable via GOOGLE_ADS_MAX_RETRIES, default 3).

Setup

Prerequisites

  • Python 3.10+
  • uv
  • A Google Ads developer token and manager account (MCC)

Install

git clone https://github.com/LucasSantana-Dev/google-ads-mcp
cd google-ads-mcp
uv sync --extra dev

Credentials

cp .env.example .env
Variable Description
GOOGLE_ADS_CLIENT_ID OAuth 2.0 Desktop App client ID
GOOGLE_ADS_CLIENT_SECRET OAuth 2.0 Desktop App client secret
GOOGLE_ADS_DEVELOPER_TOKEN Your Google Ads developer token
GOOGLE_ADS_LOGIN_CUSTOMER_ID MCC account ID (digits only, no hyphens)
GOOGLE_ADS_REFRESH_TOKEN Long-lived OAuth refresh token (see below)
GOOGLE_ADS_MUTATE_ALLOWLIST Comma-separated customer IDs that may receive writes
GOOGLE_ADS_AUDIT_LOG Audit log path (default: audit-log.jsonl)
GOOGLE_ADS_MAX_BID_CHANGE_PCT Max bid change fraction (default: 0.25)
GOOGLE_ADS_MAX_BUDGET_CHANGE_PCT Max budget change fraction (default: 0.20)
GOOGLE_ADS_MAX_RETRIES Quota retry attempts (default: 3)
Mint a refresh token
GOOGLE_ADS_CLIENT_ID=... GOOGLE_ADS_CLIENT_SECRET=... \
  uv run python scripts/get_refresh_token.py

A browser opens for OAuth consent. Copy the printed refresh token into .env.

Claude Desktop integration

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "google-ads": {
      "command": "/Users/<you>/.local/bin/uv",
      "args": [
        "run",
        "--directory", "/path/to/google-ads-mcp",
        "--env-file", "/path/to/google-ads-mcp/.env",
        "python", "-m", "google_ads_mcp.server"
      ]
    }
  }
}

Restart Claude Desktop. The 31 tools appear automatically.

Example conversations

# Reporting
"Show me all campaigns for account 123-456-7890 with spend > $100 this week"
"What search terms triggered our branded keywords in the last 7 days?"
"Any keyword ideas for 'project management software'?"
"What recommendations does Google have for my account?"

# Writes — always previews first; apply with confirm=true
"Pause campaign 9876543, it's burning budget"
"Set the CPC bid for keyword 111 in ad group 222 to $1.50"
"Create a new Search campaign 'Brand Q3' with a $20/day budget"
"Apply the 'add keyword' recommendation for campaign 555"

Development

uv run pytest                          # 102 tests, 89% coverage
uv run ruff check src/ tests/          # lint
uv run mypy src/                       # type check

CI runs on every push and pull request.

Architecture

src/google_ads_mcp/
├── server.py          # FastMCP entrypoint, tool registration
├── config.py          # GoogleAdsClient factory (reads .env)
├── gaql.py            # GAQL execution, validators, row serialization, retry
├── mutate.py          # Write-safety harness (allowlist → cap → preview → audit)
├── retry.py           # Exponential back-off for quota errors
├── errors.py          # tool_handler wrapper → {success, error} for any exception
├── testing.py         # FakeGoogleAdsClient — unit tests without network or credentials
└── tools/
    ├── accounts.py        # list_accessible_customers
    ├── campaigns.py       # get_campaigns, get_campaign_performance
    ├── adgroups.py        # get_ad_groups
    ├── keywords.py        # get_keywords, get_search_terms
    ├── changes.py         # get_change_events
    ├── query.py           # run_gaql_query
    ├── metadata.py        # get_*_metadata (3 tools)
    ├── writes_status.py   # pause/enable campaign/ad_group/keyword/ad (8 tools)
    ├── writes_value.py    # update keyword bid, ad group bid, campaign budget (3 tools)
    ├── creates.py         # create campaign, ad group, keyword, ad (4 tools)
    └── planning.py        # recommendations, keyword ideas, forecast (5 tools)

Transport: stdio only — no HTTP port, no public exposure. Auth: OAuth 2.0 Desktop App + refresh token. Credentials stay on the local machine.

License

MIT

MCP Server · Populars

MCP Server · New

    customermates

    customermates

    Open-source CRM for people and AI agents, with MCP, a company knowledge base and a self-hostable core.

    Community customermates
    alessandro-afloarei

    ShadowGit MCP server

    A Model Context Protocol (MCP) server that provides AI assistants with secure, read-only access to your ShadowGit repositories. This enables powerful debugging and code analysis capabilities by giving AI access to your project's fine-grained git history.

    Astrix-Labs

    Papez

    Open-source causal graph memory for AI agents. 85.55 on LoCoMo under a frozen, reproducible protocol. MCP server with ACT-R scoring, spreading activation, and active forgetting.

    Community Astrix-Labs
    FROWNINGdev

    ORM Lens

    ER diagrams, N+1 detection, schema drift & migration-risk linting for Django and SQLAlchemy (Alembic) — VS Code, CLI and MCP server. Static analysis: no database, no app boot. Free & MIT. Formerly django-orm-lens.

    Community FROWNINGdev
    M-Pineapple

    Dev Project Coordinator

    Local desk for AI agents: track coding and non-coding projects as JSON, with Analytics and Kanban views. MCP server, Mac-first, Docker for Linux.

    Community M-Pineapple