Google Ads MCP Server
by keysersoft
README.md
# Google Ads MCP Server
**Connect Google Ads to Claude, ChatGPT and Copilot: campaigns, ad groups, ads, keywords, search terms, budgets, conversion actions and change history as MCP tools.** Powered by [AnythingMCP](https://github.com/HelpCode-ai/anythingmcp).
Google Ads MCP Server gives Claude, ChatGPT, Copilot and Cursor 17 tools for Google Ads: campaigns, ad groups, ads, keywords, search terms, budgets, conversion actions and change history. Every tool only reads. It runs on AnythingMCP: one click on AnythingMCP Cloud, or self-hosted with Docker. Credentials are stored encrypted and every call is audited.
**Last verified:** 2026-09-27 against two production Google Ads accounts (API v25, Explorer access) (all 17 tools and the 15 playbook queries called live, the connector authorized on AnythingMCP Cloud, and an account review run from claude.ai).
**Adapter synced:** <!-- synced -->2026-09-27
Maintained by [@keysersoft](https://github.com/keysersoft), an AnythingMCP maintainer. Built on [AnythingMCP](https://github.com/HelpCode-ai/anythingmcp) by helpcode.ai.
## Quick start (AnythingMCP Cloud)
1. Sign in at [cloud.anythingmcp.com](https://cloud.anythingmcp.com) and open the [install link](https://cloud.anythingmcp.com/connectors/store?install=google-ads).
2. Enter `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` (see [Authentication](#authentication)).
3. Copy the URL of your MCP server under **MCP Servers** and add it to your AI client ([below](#connect-claude-chatgpt-copilot-or-cursor)).
AnythingMCP Cloud is the same open-source code, operated by helpcode.ai in Frankfurt, Germany.
## Self-hosted (Docker)
Needs Docker 24+, openssl and Node 18+.
```bash
git clone https://github.com/keysersoft/google-ads-mcp-server.git
cd google-ads-mcp-server
./scripts/install.sh
```
`install.sh` writes `.env` with fresh secrets, starts AnythingMCP, creates the first admin, installs the connector if `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` are set in `.env` and creates an MCP API key. Without credentials it prints the install link instead: `http://localhost:3000/connectors/store?install=google-ads`. Then check the whole chain:
```bash
npm install && node scripts/smoke.mjs
```
## Connect Claude, ChatGPT, Copilot or Cursor
- **Claude (claude.ai, Desktop, mobile):** *Customize → Connectors → Add custom connector*, paste your MCP server URL and sign in. Claude connects from Anthropic's cloud, so the URL must be public HTTPS: your AnythingMCP Cloud URL, or your own instance behind TLS.
- **Claude Code:**
```bash
claude mcp add --transport http google-ads-mcp-server http://localhost:4000/mcp --header "X-API-Key: <MCP_API_KEY>"
```
- **Cursor** (`.cursor/mcp.json`) and **VS Code / GitHub Copilot** (`.vscode/mcp.json`, key `servers` instead of `mcpServers`, plus `"type": "http"`):
```json
{ "mcpServers": { "google-ads-mcp-server": { "url": "http://localhost:4000/mcp", "headers": { "X-API-Key": "<MCP_API_KEY>" } } } }
```
- **ChatGPT:** add the public HTTPS URL as a connector (app) in ChatGPT's settings. A `localhost` URL does not work there.
## Tools
17 tools, generated from [`adapter/google-ads.json`](adapter/google-ads.json). **read** tools cannot change anything in the source system.
<!-- tools:start (generated from adapter/*.json, do not edit) -->
| Tool | What it does | Access |
|---|---|---|
| `gads_playbook` | START HERE for any Google Ads question: returns, with NO API call, a router from common questions (account health, wasted spend, search-term mining, Quality… | read |
| `gads_list_accessible_customers` | List the Google Ads accounts the authorised Google user can access directly, as resource names ('customers/1234567890'). | read |
| `gads_get_customer` | Read one Google Ads account's settings: name, currency, time zone (all dates in reports are in this zone), whether it is a manager (MCC) or test account,… | read |
| `gads_list_client_accounts` | List the accounts under a manager (MCC) account: id, name, depth in the hierarchy, whether each is itself a manager, currency, time zone and status. | read |
| `gads_run_gaql` | Run any read-only Google Ads Query Language (GAQL) query against one account: every resource, attribute, segment and metric of the Google Ads API. | read |
| `gads_query_field_catalog` | Look up Google Ads API field metadata (names, category RESOURCE/ATTRIBUTE/SEGMENT/METRIC, data type, enum values, whether selectable, filterable, sortable). | read |
| `gads_account_daily_metrics` | Account-level totals per day for a date range: impressions, clicks, cost, conversions, conversion value. | read |
| `gads_list_campaigns` | List the account's campaigns (removed ones excluded) with status, serving status, primary status and its reasons (why a campaign is limited or not serving),… | read |
| `gads_campaign_performance` | Campaign performance totals for a date range, one row per campaign, sorted by cost: impressions, clicks, CTR, average CPC, cost, conversions, conversion… | read |
| `gads_ad_group_performance` | Ad group performance totals for a date range, one row per ad group with its campaign, sorted by cost. | read |
| `gads_ad_performance` | Ad performance for a date range with the ad itself: type, final URLs, responsive search ad headlines and descriptions, ad strength and policy approval status. | read |
| `gads_keyword_performance` | Keyword performance for a date range with match type, status and Quality Score components (quality score 1-10, expected CTR, ad relevance, landing page… | read |
| `gads_search_terms` | The search terms report: what people actually typed before your Search ads showed, with campaign, ad group, whether the term is already added or excluded,… | read |
| `gads_budget_pacing` | Enabled campaigns with their daily budget, spend in the date range and Search impression share lost to budget and to rank. | read |
| `gads_conversion_actions` | List the account's conversion actions (removed ones excluded): name, type, source, category, status, whether each is a primary goal, counting (one vs… | read |
| `gads_change_history` | Who changed what in the account and when: timestamp, user email, tool used (web UI, API, scripts, Editor), resource type and name, operation and the changed… | read |
| `gads_recommendations` | Google's current optimisation recommendations for the account (budget, bidding, keywords, ads, assets...) with the campaign they apply to and the estimated… | read |
<!-- tools:end -->
## Example prompts
- How did my Google Ads account do in the last 30 days? Spend, conversions, cost per conversion and the main campaigns.
- Compare this month with last month by campaign: spend, conversions, CPA and ROAS.
- List the search terms from the last 30 days that cost money and never converted, and group them into negative keyword ideas.
- Which keywords have a Quality Score of 5 or lower, and which component is below average?
- Are my Search campaigns losing impression share to budget or to rank?
- Which ads are disapproved or have poor ad strength?
More in [examples/prompts.md](examples/prompts.md).
## Authentication
The connector signs in with OAuth 2.0 as a Google user. You need an OAuth client from a Google Cloud project that has Google Ads API access:
| Variable | Where to find it |
|---|---|
| `GOOGLE_CLIENT_ID` | Google Cloud console → **APIs & Services → Credentials** → OAuth client ID (type **Web application**) |
| `GOOGLE_CLIENT_SECRET` | the same OAuth client |
| `GOOGLE_ADS_LOGIN_CUSTOMER_ID` (optional) | the 10-digit id of the manager (MCC) account, only when you reach client accounts through a manager |
1. Enable **Google Ads API** in the project and check its access level on the project's Google Ads API page. A new project starts at **Test** (test accounts only); request **Explorer** to read real accounts. Since 9 September 2026 no developer token is needed.
2. Add the redirect URI `<your AnythingMCP URL>/api/mcp-oauth/callback` to the OAuth client (on AnythingMCP Cloud: `https://cloud.anythingmcp.com/api/mcp-oauth/callback`), and set the OAuth consent screen to **In production** so the refresh token does not expire after 7 days.
3. Install the connector with the client ID and secret, then click **Authorize with Provider** on the connector page. The refresh token is stored encrypted and access tokens are renewed automatically.
Google Ads has a single OAuth scope (`adwords`) and it is not read-only. The connector exposes only read tools; authorizing a Google Ads user with the *Read only* role adds a second safeguard.
## Security
- **Read or write is your choice.** All 17 tools only read. Assign the connector to an MCP server whose role whitelists only the tools you want, and the rest are invisible to that client.
- **Credentials** are encrypted with AES-256-GCM and never shown to the model.
- **Response mapping** drops or reshapes fields per tool before they reach the model, e.g. bank details or personal data.
- **Audit log:** every call is recorded with input, output, duration and status, in your own database when self-hosted.
- **SSO, RBAC and SCIM** are included in the self-hosted build.
## FAQ
### Do I need a Google Ads developer token?
No. Google retired developer tokens on 9 September 2026. API access now depends on the access level of your Google Cloud project: Test reaches only test accounts, Explorer (2,880 operations a day) reads real accounts, Basic and Standard raise the quota.
### How do I use Google Ads in Claude or ChatGPT?
Install the Google Ads connector on AnythingMCP, authorize it once in the browser, then add the AnythingMCP server URL to Claude as a custom connector or to ChatGPT as a developer-mode app. Both sign in with OAuth, so nothing runs on your computer. Claude Code, Cursor and other clients use the same URL with an API key header.
### Can the AI change my campaigns?
No. All 17 tools read: reports, lists and GAQL queries. There is no tool to pause, create or edit anything, and every tool is annotated as read-only for the client.
### Which reports are covered?
Account settings and manager hierarchies, daily account metrics, campaign, ad group, ad and keyword performance with Quality Score components, the search terms report, budget pacing with impression share lost to budget and rank, conversion actions, change history and recommendations. Anything else goes through `gads_run_gaql`, and `gads_playbook` gives the model tested queries for device, location, hour, landing page, Performance Max, asset and audience reports.
### Does it work with manager (MCC) accounts?
Yes. Set `GOOGLE_ADS_LOGIN_CUSTOMER_ID` to the manager's id; `gads_list_client_accounts` lists the accounts below it.
### How much quota does a question use?
A report is one operation. Answering a question usually takes 2 to 8 calls, well inside the 2,880 operations a day of Explorer access. The quota belongs to the Cloud project, so connectors sharing one OAuth client share it.
## Troubleshooting
| Problem | Fix |
|---|---|
| `401` / `403` from the vendor | The credentials are wrong or lack rights. Re-enter them on the connector page; the import runs a test call and shows the result. |
| Tools missing in the AI client | The connector is not assigned to the MCP server the client uses. Check **MCP Servers**, then run `node scripts/smoke.mjs`. |
| The host is on your internal network | Self-host AnythingMCP on that network and add the hostname to `SSRF_ALLOWED_HOSTS`, or the outbound guard blocks the call. |
| Works locally, fails on AnythingMCP Cloud | The system must be reachable from the internet with a valid TLS certificate. |
| Symptom | Cause and fix |
| `CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION` | The Cloud project behind the OAuth client still has Test access. Request Explorer access on the project's Google Ads API page. |
| `USER_PERMISSION_DENIED` on a client account | You reach the account through a manager: set `GOOGLE_ADS_LOGIN_CUSTOMER_ID` to that manager's id, or authorize a user with direct access. |
| `401` or `invalid_grant` | The refresh token was revoked or expired (an OAuth consent screen in Testing expires it after 7 days). Set the consent screen to In production and click **Authorize with Provider** again. |
| `UNRECOGNIZED_FIELD` or `PROHIBITED_*` from `gads_run_gaql` | A field name is wrong or does not combine with the resource. Check it with `gads_query_field_catalog`. |
| `RESOURCE_EXHAUSTED` | The daily or per-minute quota of the Cloud project is used up. Wait, or request a higher access level. |
| Campaign rows with zero metrics | Paused or idle campaigns are returned too and sort last; they are not an error. |
| Amounts look a million times too large | Money fields are in micros of the account currency: divide by 1,000,000. |
## Related
- [openapi-to-mcp](https://github.com/HelpCode-ai/openapi-to-mcp): OpenAPI to MCP: turn any OpenAPI/Swagger or REST API into an MCP server for Claude & ChatGPT. Every endpoint a tool, no code.
- [jev-mcp-server](https://github.com/keysersoft/jev-mcp-server): Jev MCP server: use TypeSafe Jev in Claude & ChatGPT. Yes/no, classification and scoring with probabilities, hosted or self-hosted.
- [AnythingMCP](https://github.com/HelpCode-ai/anythingmcp): the open-source MCP server and gateway this repository is built on.
## License
AGPL-3.0-only. The adapter definition in `adapter/` comes from AnythingMCP (AGPL-3.0).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues