io.github.mharnett/google-ads
Google Ads MCP with MCC support: 35 tools for campaigns, keywords, reporting, GAQL.
Ask AI about io.github.mharnett/google-ads
Powered by Claude Β· Grounded in docs
I know everything about io.github.mharnett/google-ads. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
MCP Google Ads Server
An MCP (Model Context Protocol) server for the Google Ads API with built-in safeguards for review before changes go live. Production-proven with MCC (Manager Account) support, 36 tools for campaign management, reporting, and optimization. v1.2.0 adds Demand Gen campaign creation end-to-end.
Features
- MCC Support: Works with Manager accounts and multiple client accounts
- Auto-Context: Detects which client account based on your working directory
- Safe by Default: All new items created in PAUSED state
- Approval Workflow: Enable items only after manual review
- Validation: Validates ads before creating to catch errors early
- Resilience: Circuit breakers, retry with backoff, and timeout handling (cockatiel)
- Structured Logging: Pino-based logging with build fingerprinting
Setup
1. Google Ads API Access
You need:
- A Google Ads Developer Token (apply at Google Ads API Center)
- OAuth credentials (Client ID & Secret from Google Cloud Console)
- A Refresh Token for your MCC account
Getting OAuth Credentials
- Go to Google Cloud Console
- Create a project or select existing
- Enable the Google Ads API
- Go to Credentials β Create Credentials β OAuth Client ID
- Choose Desktop App
- Download the JSON (contains client_id and client_secret)
Getting a Refresh Token
Use the Google OAuth playground or run:
pip install google-ads
google-ads-auth
2. Install
npm install mcp-google-ads
Or clone and build from source:
git clone https://github.com/mharnett/mcp-google-ads.git
cd mcp-google-ads
npm install
npm run build
Security: Never share your .mcp.json file or commit it to git -- it may contain API credentials. Add .mcp.json to your .gitignore.
3. Configure
cp config.example.json config.json
Edit config.json with your credentials:
{
"google_ads": {
"developer_token": "YOUR_DEVELOPER_TOKEN",
"client_id": "YOUR_CLIENT_ID.apps.googleusercontent.com",
"client_secret": "YOUR_CLIENT_SECRET",
"refresh_token": "YOUR_REFRESH_TOKEN",
"mcc_customer_id": "123-456-7890"
},
"clients": {
"my-client": {
"customer_id": "111-222-3333",
"name": "My Client",
"folder": "/path/to/client/workspace"
},
"another-client": {
"customer_id": "444-555-6666",
"name": "Another Client",
"folder": "/path/to/another/workspace"
}
},
"defaults": {
"create_paused": true,
"label_prefix": "claude-",
"require_approval_for_enable": true
}
}
Environment Variables
Alternatively, set credentials via environment variables (these override config.json):
| Variable | Required | Description |
|---|---|---|
GOOGLE_ADS_DEVELOPER_TOKEN | Yes | Google Ads API developer token |
GOOGLE_ADS_CLIENT_ID | Yes | OAuth 2.0 client ID |
GOOGLE_ADS_CLIENT_SECRET | Yes | OAuth 2.0 client secret |
GOOGLE_ADS_REFRESH_TOKEN | Yes | OAuth 2.0 refresh token |
GOOGLE_ADS_MCP_WRITE | No | Set to true to expose mutating tools (create/update/pause/enable/remove/apply). Default: read-only. |
Read-only by default
The server ships read-only. Mutating tools (anything that creates, updates,
pauses, enables, removes, links, or applies) are hidden from the tool list
until you set GOOGLE_ADS_MCP_WRITE=true in the MCP server environment.
If a write tool is somehow invoked without that flag, the server returns a
clear error pointing at the env var.
This is deliberate: a casual chat message like "activate the Fundraising campaign" should not move live ad spend without an explicit opt-in.
4. Add to Claude Code
Add to your Claude Code MCP settings (~/.claude/settings.json or project settings):
{
"mcpServers": {
"google-ads": {
"command": "node",
"args": ["node_modules/mcp-google-ads/dist/index.js"]
}
}
}
Or if installed from source:
{
"mcpServers": {
"google-ads": {
"command": "node",
"args": ["/path/to/mcp-google-ads/dist/index.js"]
}
}
}
Restart Claude Code.
Claude Desktop: Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows).
Usage
Workflow
1. cd into client folder β auto-detects account context
2. Ask Claude to create campaigns/ads β all created PAUSED
3. Review in Google Ads UI or Editor
4. Tell Claude to enable approved items
5. Claude enables (requires your approval prompt)
Available Tools (36)
Context & Discovery
| Tool | Description |
|---|---|
google_ads_get_client_context | Detect which account from working directory |
google_ads_list_campaigns | List all campaigns with status and metrics |
google_ads_list_ad_groups | List ad groups in a campaign |
google_ads_list_pending_changes | Show paused items with claude- label |
google_ads_list_conversion_actions | List conversion actions |
Campaign Management
| Tool | Description |
|---|---|
google_ads_create_campaign | Create campaign (PAUSED). Supports SEARCH + DEMAND_GEN channels, richer bidding (MANUAL_CPC / MAXIMIZE_CLICKS / MAXIMIZE_CONVERSIONS / TARGET_CPA), geo + language targeting, start/end dates |
google_ads_create_ad_group | Create ad group (PAUSED). type accepts SEARCH_STANDARD (default) or DEMAND_GEN_MULTI_ASSET_AD_GROUP |
google_ads_create_responsive_search_ad | Create RSA with validation (PAUSED) |
google_ads_create_image_asset | Upload PNG/JPG/GIF image asset (validates β€5MB, β₯600Γ314) for use in Demand Gen ads |
google_ads_create_demand_gen_multi_asset_ad | Create a Demand Gen multi-asset ad (PAUSED) β validates char/count caps before API call, fails fast if ad_group isn't DG |
google_ads_create_keywords | Create keywords (PAUSED) |
google_ads_validate_ad | Validate RSA without creating |
google_ads_enable_items | Enable items (make LIVE) β requires approval |
google_ads_pause_items | Pause active items |
google_ads_pause_keywords | Pause specific keywords |
google_ads_update_campaign_budget | Update campaign daily budget |
Tracking & URLs
| Tool | Description |
|---|---|
google_ads_get_campaign_tracking | Get tracking templates and URL parameters |
google_ads_update_campaign_tracking | Update tracking templates |
Negative Keywords
| Tool | Description |
|---|---|
google_ads_create_shared_set | Create shared negative keyword list |
google_ads_link_shared_set | Link shared set to campaign |
google_ads_unlink_shared_set | Unlink shared set from campaign |
google_ads_add_shared_negatives | Add keywords to shared negative list |
google_ads_remove_shared_negatives | Remove keywords from shared list |
google_ads_add_campaign_negatives | Add campaign-level negatives |
google_ads_remove_campaign_negatives | Remove campaign-level negatives |
google_ads_remove_adgroup_negatives | Remove ad group-level negatives |
Performance & Reporting
| Tool | Description |
|---|---|
google_ads_keyword_performance | Keyword metrics with quality score |
google_ads_keyword_performance_by_conversion | Keyword metrics by conversion action |
google_ads_ad_performance | Ad-level performance metrics |
google_ads_ad_performance_by_conversion | Ad metrics by conversion action |
google_ads_search_term_report | Search term query report |
google_ads_search_term_report_by_conversion | Search terms by conversion action |
google_ads_search_term_insights | Search term category insights |
google_ads_search_term_insight_terms | Terms within insight categories |
google_ads_keyword_volume | Keyword planner volume estimates |
Advanced
| Tool | Description |
|---|---|
google_ads_gaql_query | Run raw GAQL queries |
Example Commands
# Check which account you're working with
"What Google Ads account am I connected to?"
# List campaigns
"Show me all campaigns in this account"
# Create a new campaign
"Create a Search campaign for brand terms with $50/day budget"
# Check what's pending review
"What changes are pending my review?"
# After reviewing in Google Ads UI
"Enable the approved ads in the Brand campaign"
# Performance analysis
"Show me keyword performance for the last 30 days, sorted by cost"
# Run custom GAQL
"Run a GAQL query to get all ad groups with CTR below 2%"
Example: Create a Demand Gen Campaign End-to-End
# 1. Campaign: $75/day, DEMAND_GEN channel, MAXIMIZE_CONVERSIONS default,
# targeting Alaska (21134) + Maine (21141) in English
google_ads_create_campaign({
name: "DG - Spring Promo",
daily_budget: 75,
channel_type: "DEMAND_GEN",
geo_target_ids: ["21134", "21141"],
start_date: "2026-05-01",
end_date: "2026-06-30"
})
# β campaign_id: 555123
# 2. Ad group: DEMAND_GEN_MULTI_ASSET_AD_GROUP
google_ads_create_ad_group({
campaign_id: "555123",
name: "DG AG 1",
type: "DEMAND_GEN_MULTI_ASSET_AD_GROUP"
})
# β ad_group_id: 555456
# 3. Image assets (PNG/JPG/GIF, β₯600Γ314, β€5MB). Returns {asset_id, ...}
google_ads_create_image_asset({ name: "hero-landscape", file_path: "/abs/path/hero.png" })
# β asset_id: 42001
google_ads_create_image_asset({ name: "hero-square", file_path: "/abs/path/square.png" })
# β asset_id: 42002
google_ads_create_image_asset({ name: "logo", file_path: "/abs/path/logo.png" })
# β asset_id: 42003
# 4. Demand Gen multi-asset ad (PAUSED). Validates char + count caps first.
google_ads_create_demand_gen_multi_asset_ad({
ad_group_id: "555456",
final_urls: ["https://example.com/spring"],
business_name: "Example Org",
call_to_action: "LEARN_MORE",
marketing_image_asset_ids: ["42001"], // 1.91:1 landscape, β₯1 required
square_marketing_image_asset_ids: ["42002"], // 1:1 optional
logo_image_asset_ids: ["42003"], // logo optional
headlines: ["Spring Sale Now On", "Save 20% Today"], // max 5, β€40 chars each
long_headlines: ["A longer pitch under ninety characters."], // max 5, β€90 chars
descriptions: ["Shop the latest looks.", "Free returns."] // max 5, β€90 chars each
})
# β resource_name: customers/.../adGroupAds/555456~67890000
After all four calls the campaign, ad group, and ad all live in your account in PAUSED state and are labeled Claude-MM-DD-YY. Review in the Google Ads UI, then enable via google_ads_enable_items.
Safety Features
- Everything starts PAUSED β Nothing goes live until you explicitly enable it
- Label tracking β All Claude-created items get a
claude-pendinglabel - Validation β Ads are validated before creation (headline/description lengths, etc.)
- Approval prompts β The
enable_itemstool requires explicit approval in Claude Code - Client isolation β Working directory determines which account, preventing cross-client mistakes
Adding New Clients
Edit config.json to add clients. Map each client to a working directory:
{
"clients": {
"client-slug": {
"customer_id": "123-456-7890",
"name": "Client Name",
"folder": "/path/to/client/workspace"
}
}
}
No server restart needed β config is read on each request.
Troubleshooting
"No client found for working directory"
- Make sure you're in a folder that matches one of your
clientsentries - Check that the folder path in config.json matches exactly
"Developer token not approved"
- New developer tokens need approval from Google
- Use a test account while waiting for approval
"Authentication failed"
- Refresh token may be expired β regenerate it
- Check that client_id and client_secret are correct
License
MIT β see LICENSE for details.
