{
  "markdown": "# google-ads-mcp-unofficial\n\n[![CI](https://img.shields.io/badge/CI-passing-22C55E?style=flat-square)](https://github.com/davidmosiah/google-ads-mcp-unofficial/actions)\n[![npm](https://img.shields.io/npm/v/google-ads-mcp-unofficial?style=flat-square&color=cb3837&logo=npm)](https://www.npmjs.com/package/google-ads-mcp-unofficial)\n[![npm downloads](https://img.shields.io/npm/dm/google-ads-mcp-unofficial?style=flat-square&color=cb3837)](https://www.npmjs.com/package/google-ads-mcp-unofficial)\n[![MIT](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)\n[![MCP](https://img.shields.io/badge/MCP-compliant-7C3AED?style=flat-square)](https://modelcontextprotocol.io)\n\n> Unofficial Model Context Protocol server that lets AI agents read, analyze, and (gated) act on a Google Ads account — pause keywords, set bids, adjust budgets — without leaving your stack.\n\n**Unofficial. Not affiliated with Google.** This is a local-first MCP server that speaks the Google Ads REST API directly. It does NOT include automated financial-account changes beyond what the Google Ads API allows. Always review proposed changes before enabling `GOOGLE_ADS_ALLOW_MUTATIONS`.\n\n---\n\n## HTTP (v2 stateless)\n\nDefault is **stdio**. Optional Streamable HTTP — no session id, JSON responses, loopback only:\n\n```bash\nnpx -y google-ads-mcp-unofficial --http\n# GET  http://127.0.0.1:3000/health\n# POST http://127.0.0.1:3000/mcp   (sessionless)\n```\n\nEnv: `GOOGLE_ADS_MCP_HOST`, `GOOGLE_ADS_MCP_PORT`, `GOOGLE_ADS_MCP_TRANSPORT=http`.\n\n\n## Quick start (30 seconds)\n\n```bash\nnpx -y google-ads-mcp-unofficial setup\n```\n\nThe setup wizard collects your developer token + OAuth client, writes `~/.google-ads-mcp/config.json` (chmod 600), then walks you through the OAuth dance via a local callback at `http://127.0.0.1:3000/callback`.\n\nVerify:\n\n```bash\nnpx -y google-ads-mcp-unofficial doctor\n```\n\nThen add it to your agent (Claude Desktop, Cursor, Hermes, OpenClaw, Codex — see examples below).\n\n---\n\n## What it does\n\n22 tools across 6 categories.\n\n| Category | Count | Examples |\n|---|---|---|\n| **Meta / diagnostic** | 5 | `google_ads_connection_status`, `google_ads_capabilities`, `google_ads_agent_manifest`, `google_ads_data_inventory`, `google_ads_privacy_audit` |\n| **Shared Delx profile** | 3 | `google_ads_profile_get`, `google_ads_profile_update`, `google_ads_onboarding` |\n| **Auth** | 3 | `google_ads_get_auth_url`, `google_ads_exchange_code`, `google_ads_revoke_access` |\n| **Reads** (always safe) | 8 | `google_ads_list_accounts`, `google_ads_list_campaigns`, `google_ads_get_campaign`, `google_ads_list_ad_groups`, `google_ads_list_keywords`, `google_ads_get_account_performance`, `google_ads_get_campaign_performance`, `google_ads_get_keyword_performance` |\n| **Workflow** | 2 | `google_ads_daily_report`, `google_ads_find_waste` |\n| **Mutations** (gated) | 6 | `google_ads_pause_keyword`, `google_ads_resume_keyword`, `google_ads_set_keyword_bid_micros`, `google_ads_set_campaign_budget_micros`, `google_ads_pause_campaign`, `google_ads_resume_campaign` |\n\nFull tool reference: see `AGENTS.md`.\n\n---\n\n## Setup wizard\n\n```bash\nnpx -y google-ads-mcp-unofficial setup [--allow-mutations] [--client hermes|claude|cursor|...]\n```\n\nWhat it does:\n\n1. Prompts for: developer token, OAuth client id/secret, optional login_customer_id (MCC), redirect URI, privacy mode.\n2. Writes `~/.google-ads-mcp/config.json` with `chmod 600`.\n3. Writes an MCP client config (e.g. merges into `claude_desktop_config.json` on macOS; writes a Hermes block and skill file for `--client hermes`).\n4. Runs the OAuth dance (unless `--no-auth`) by opening Google's consent screen and listening on `127.0.0.1:3000`.\n\n> **Mutations are off by default.** `--allow-mutations` enables write tools. ASK THE USER before turning this on — it lets agents change campaigns, bids, budgets, and pause/resume keywords.\n\n---\n\n## Auth model\n\nThis MCP requires **two credentials**:\n\n1. **Google Ads Developer Token** — from your MCC (Manager) account at https://ads.google.com/aw/apicenter. New tokens start in \"Test account access\" mode and need approval for production traffic.\n2. **Google OAuth 2.0 Client** — a \"Desktop\" or \"Web\" client created in Google Cloud Console. The single OAuth scope used is `https://www.googleapis.com/auth/adwords`.\n\n> ⚠️ **Refresh token gotcha:** Google only returns a `refresh_token` on first consent or after you revoke the prior grant at https://myaccount.google.com/permissions. Our `auth` flow uses `prompt=consent` to maximize the chance Google returns one — but if your code-exchange response is missing `refresh_token`, the tool will tell you to revoke and retry.\n\n| Variable | Purpose | Stored where | Secret? |\n|---|---|---|---|\n| `GOOGLE_ADS_DEVELOPER_TOKEN` | Approved developer token | `~/.google-ads-mcp/config.json` or env | **yes** |\n| `GOOGLE_ADS_CLIENT_ID` | OAuth client id | local or env | no |\n| `GOOGLE_ADS_CLIENT_SECRET` | OAuth client secret | local or env | **yes** |\n| `GOOGLE_ADS_LOGIN_CUSTOMER_ID` | MCC id (no dashes) | local or env | no |\n| `GOOGLE_ADS_REDIRECT_URI` | OAuth callback | local or env (default `http://127.0.0.1:3000/callback`) | no |\n| `GOOGLE_ADS_PRIVACY_MODE` | `summary` \\| `structured` \\| `raw` | local or env (default `structured`) | no |\n| `GOOGLE_ADS_ALLOW_MUTATIONS` | Enable write tools | local or env (default `false`) | no |\n| `GOOGLE_ADS_TOKEN_PATH` | Override token storage | local or env (default `~/.google-ads-mcp/tokens.json`) | no |\n| `GOOGLE_ADS_CACHE` | Enable SQLite cache | local or env (default off) | no |\n| `GOOGLE_ADS_CACHE_PATH` | Override cache path | local or env | no |\n| `GOOGLE_ADS_NO_RETRY` | Disable retry middleware | env (default off) | no |\n\n---\n\n## Privacy modes\n\n| Mode | What you get | Customer id |\n|---|---|---|\n| `summary` | id + name + status fields only | partial-redacted (`123-***-7890`) |\n| `structured` (default) | flat normalized rows with metrics | partial-redacted |\n| `raw` | full upstream Google Ads REST payload | **full** |\n\nPass `privacy_mode` per call to override the default per response.\n\n**Redaction matrix:**\n\n| Field | summary | structured | raw |\n|---|---|---|---|\n| developer_token | n/a | n/a | n/a (never returned) |\n| access_token, refresh_token, client_secret | [REDACTED] in all errors | [REDACTED] | [REDACTED] |\n| email addresses in error messages | [REDACTED] | [REDACTED] | [REDACTED] |\n| customer_id | `123-***-7890` | `123-***-7890` | full |\n| metrics (clicks, cost, etc.) | dropped | included | included |\n\n---\n\n## ⚠️ Mutation gating — read this\n\n**Default: mutations are DISABLED.** Six tools are gated:\n\n- `google_ads_pause_keyword` / `google_ads_resume_keyword`\n- `google_ads_set_keyword_bid_micros`\n- `google_ads_set_campaign_budget_micros`\n- `google_ads_pause_campaign` / `google_ads_resume_campaign`\n\nIf an agent calls any of them without `GOOGLE_ADS_ALLOW_MUTATIONS` enabled, it gets:\n\n```\nError: Write tools are disabled. To enable: re-run `google-ads-mcp-server setup --allow-mutations`\nor enable GOOGLE_ADS_ALLOW_MUTATIONS. ASK THE USER BEFORE TURNING THIS ON — it lets agents\nchange campaigns, bids, budgets, and pause/resume keywords.\n```\n\nEven with the env enabled, **every mutation requires `explicit_user_intent: true`** in the per-call arguments. And every mutation is logged to stderr with the resource name:\n\n```\n[google-ads-mcp] MUTATION pause_keyword {\"resource_name\":\"customers/1234567890/adGroupCriteria/999~111222333\"}\n```\n\nRead `SECURITY.md` for the threat model.\n\n---\n\n## Agent client examples\n\n### Claude Desktop\n\n`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):\n\n```json\n{\n  \"mcpServers\": {\n    \"google-ads\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"google-ads-mcp-unofficial\"]\n    }\n  }\n}\n```\n\nThen restart Claude Desktop.\n\n### Cursor / Windsurf\n\n`~/.cursor/mcp.json` (or your IDE equivalent):\n\n```json\n{\n  \"mcpServers\": {\n    \"google-ads\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"google-ads-mcp-unofficial@0.1.0\"]\n    }\n  }\n}\n```\n\n### Hermes\n\n```yaml\n# ~/.hermes/config.yaml\nmcp_servers:\n  google-ads:\n    command: npx\n    args:\n      - -y\n      - google-ads-mcp-unofficial@0.1.0\n    timeout: 120\n    connect_timeout: 60\n    sampling:\n      enabled: false\n```\n\nThen `/reload-mcp` (do NOT restart the gateway for normal data access).\n\n### OpenClaw\n\n`~/.openclaw/mcp.servers.json`:\n\n```json\n{\n  \"google-ads\": {\n    \"command\": \"npx\",\n    \"args\": [\"-y\", \"google-ads-mcp-unofficial@0.1.0\"]\n  }\n}\n```\n\n### Codex / TOML clients\n\nSee `examples/codex.toml` for the equivalent TOML block.\n\n---\n\n## Workflow examples\n\n### 1. Daily performance pulse\n\n```jsonc\n// Agent asks: \"How did my Google Ads do yesterday?\"\n{\n  \"name\": \"google_ads_daily_report\",\n  \"arguments\": {\n    \"customer_id\": \"1234567890\",\n    \"cpc_alert_threshold\": 0.15\n  }\n}\n```\n\nReturns markdown with yesterday + 7d + 30d aggregates. If yesterday's CPC exceeds 0.15, the output gets an `ALERT` banner.\n\n### 2. Identify waste (read-only)\n\n```jsonc\n{\n  \"name\": \"google_ads_find_waste\",\n  \"arguments\": {\n    \"customer_id\": \"1234567890\",\n    \"date_range\": \"LAST_30_DAYS\",\n    \"min_clicks\": 5,\n    \"min_cost_micros\": 200000,\n    \"zero_conversions_only\": true\n  }\n}\n```\n\nReturns a ranked list of keywords matching \"spent ≥ $0.20 with ≥5 clicks and 0 conversions\" — but never pauses them.\n\n### 3. Pause a single keyword (mutation, gated)\n\nAfter the user confirms:\n\n```jsonc\n{\n  \"name\": \"google_ads_pause_keyword\",\n  \"arguments\": {\n    \"customer_id\": \"1234567890\",\n    \"ad_group_id\": \"999\",\n    \"criterion_id\": \"111222333\",\n    \"explicit_user_intent\": true\n  }\n}\n```\n\n---\n\n## Troubleshooting\n\n| Symptom | Action |\n|---|---|\n| `Missing required Google Ads environment variables` | Run `setup` or set the env vars listed in the error. |\n| `Google did not return a refresh_token` | Revoke at https://myaccount.google.com/permissions then re-run `auth`. |\n| `PERMISSION_DENIED` on a read | Confirm `GOOGLE_ADS_LOGIN_CUSTOMER_ID` matches the MCC that owns the target customer (no dashes). |\n| `Write tools are disabled` | Expected. Ask the user before enabling `GOOGLE_ADS_ALLOW_MUTATIONS`. |\n| Hermes tools missing after config edit | `/reload-mcp` or `hermes mcp test google-ads`. Do NOT restart the gateway. |\n| Token file insecure perms warning | `chmod 600 ~/.google-ads-mcp/tokens.json` |\n\n---\n\n## Support\n\n- GitHub Issues: https://github.com/davidmosiah/google-ads-mcp-unofficial/issues\n- Email: support@delx.ai\n- X: [@delx369](https://x.com/delx369)\n\n---\n\n## Disclaimer\n\nUnofficial integration. **Not affiliated with Google.** This MCP does not include automated financial-account changes beyond what the Google Ads REST API allows. Always review proposed changes before enabling `GOOGLE_ADS_ALLOW_MUTATIONS`. The author is not responsible for budget overruns, paused campaigns, or any other consequences of automated changes to your Google Ads account.\n\nMIT-licensed.\n\n## Skill or MCP\n\nSame package, two doors. MCP registers tools on stdio/HTTP. The [skill](skill/SKILL.md) can drive the **same** tools through the CLI when the client has no MCP:\n\n```bash\nnpx -y google-ads-mcp-unofficial call google_ads_connection_status --json '{}'\n```\n\nCopy `skill/SKILL.md` into your agent skills dir.\n",
  "bytes": 11221,
  "sha": "4c5fca254ae25a0eab6290296ea5ae2b94da1f4a5908ad4c1b8a0e6eeab2dd4a",
  "repo_slug": "davidmosiah/google-ads-mcp-unofficial",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_davidmosiah_google_ads_mcp_652970a3/readme"
}