google-ads-full-readwrite-mcp
v0.3.0
Published
MCP server for Google Ads with full read and write access — manage campaigns, ads, assets, budgets, and keywords from any MCP host.
Maintainers
Readme
google-ads-full-readwrite-mcp
A Model Context Protocol (MCP) server that exposes the Google Ads API as a set of LLM-callable tools. Read and write surface covers campaigns, ad groups, ads, assets, Performance Max asset groups, budgets, bidding strategies, keywords, recommendations, conversion-action listing, and arbitrary GAQL queries.
Why this exists
Managing Google Ads campaigns by hand in the web UI is slow and clicky. This MCP lets any MCP-aware host (Claude Desktop, IDE agents, etc.) operate Google Ads from natural-language prompts: build a campaign, set budgets, add keywords, write ads, attach assets, and review performance, all from chat.
Existing MCP options for Google Ads are either read-only, proprietary, or incomplete on the write surface. This package is open-source (MIT), Node-packageable, write-capable across the routine surface of the Ads API, and installable via npx with no SaaS gateway or paid hosting.
Requirements
- Node.js 18 or newer.
- A Google Ads manager (MCC) account with a developer token at Explorer level or higher.
- A Google Ads operating customer account linked to that MCC.
- A Google Cloud project (free; no billing required) with the Google Ads API enabled and an OAuth 2.0 client of type Desktop application.
- A refresh token minted via the bundled
--loginflow.
If you don't have all of these yet, the One-time setup section below walks through each one from scratch.
One-time setup
The pre-flight checklist has seven steps. The first one (developer token approval) takes the longest, so start it first.
1. Apply for a Google Ads developer token
The developer token lets your code call the Google Ads API at all. It is issued to a manager (MCC) account, not to an individual user.
- If you do not yet have an MCC, create one at https://ads.google.com/home/tools/manager-accounts/.
- Sign into the MCC. Tools & Settings → API Center → Apply for a token.
- Fill in the application form. For non-production work against test accounts only, you can request a Test access token (issued instantly). For real Google Ads accounts you need at minimum Explorer access (entry-level production tier). Google reviews production-tier applications and typically responds in 1–2 business days.
Record the token value for GOOGLE_ADS_DEVELOPER_TOKEN.
2. Link your operating customer account to the MCC
The customer account whose campaigns you want to manage must be a sub-account of the MCC that owns the developer token.
- From the MCC, go to Sub-account settings.
- Click the blue + button → Link existing account, and enter the operating customer ID.
- Sign into the operating customer account in a separate tab and accept the invitation under Admin → Access and security → Managers (newer UI) or Tools & Settings → Setup → Account access → Managers (older UI).
- Back in the MCC's Sub-account settings, confirm the operating account shows as Active.
3. Create a Google Cloud project and enable the Google Ads API
- Open https://console.cloud.google.com. Sign in with the Google account that will own the OAuth client. (This account must also have access to the Google Ads account you want to manage.)
- Top bar → project picker → New Project. Name it anything (e.g.
google-ads-mcp). - Switch into the new project via the picker.
- Hamburger menu → APIs & Services → Library.
- Search for Google Ads API, click the result, click Enable.
No billing setup is required. The Google Ads API is free; the Cloud project is just a container for the OAuth credentials.
4. Configure the OAuth consent screen
Google requires this before issuing OAuth client credentials. Hamburger menu → APIs & Services → OAuth consent screen.
Google has been migrating between two layouts. Follow whichever you see.
Older layout (User Type radio buttons):
- Select External. Click Create.
- App name: anything (e.g. "Google Ads MCP"). User support email: your email. Developer contact email: your email. Skip everything else.
- Save and continue. On the Scopes page, save and continue without adding anything. (Scopes are requested at runtime by the code.)
- Test users page: click + Add Users, add the Google account you will authenticate with during
--login. Save and continue. - Leave publishing status as Testing.
Newer layout (Branding / Audience / Data Access tabs):
- Click Get started. Enter app name and support email. Audience: External. Contact info. Agree to the policy and Create.
- Open the Audience tab. Under Test users, click + Add users and add the Google account you will authenticate with during
--login. Save. - Leave publishing status as Testing.
5. Create the OAuth Desktop client
- APIs & Services → Credentials → + Create Credentials → OAuth client ID.
- Application type: Desktop app. Name it anything.
- Save the client ID and client secret. You will pass them to the MCP as
GOOGLE_ADS_CLIENT_IDandGOOGLE_ADS_CLIENT_SECRET.
6. Mint a refresh token
Run the bundled login flow. From a shell with the client ID and secret in env vars:
export GOOGLE_ADS_CLIENT_ID=...
export GOOGLE_ADS_CLIENT_SECRET=...
npx -y google-ads-full-readwrite-mcp --loginOn PowerShell, set env vars with $env:GOOGLE_ADS_CLIENT_ID = "..." instead of export.
The flow opens Google's consent screen in your default browser, captures the authorization code on a loopback redirect (http://127.0.0.1:<random-port>/oauth2callback), exchanges it for a refresh token, and prints the refresh token to stdout.
You will see a warning along the lines of "Google hasn't verified this app." That is expected when the consent screen is in Testing mode (which it is by default). Click Advanced → Go to (unsafe) → Continue and grant the requested scopes. You are the developer of this OAuth client, so this is safe in your own setup.
Copy the printed refresh token into your MCP host config as GOOGLE_ADS_REFRESH_TOKEN.
If you prefer to persist the refresh token to disk, run npx -y google-ads-full-readwrite-mcp --login --save. The token is written to ${LOCALAPPDATA}\google-ads-full-readwrite-mcp\auth.json on Windows or ~/.config/google-ads-full-readwrite-mcp/auth.json elsewhere, mode 0600. The server reads that file as a fallback when GOOGLE_ADS_REFRESH_TOKEN is not in the environment.
7. Note the two customer IDs
The MCP needs two ten-digit IDs, both with dashes removed.
- MCC ID (
GOOGLE_ADS_LOGIN_CUSTOMER_ID) is shown in the top-right of any MCC view.123-456-7890becomes1234567890. - Operating account ID (
GOOGLE_ADS_CUSTOMER_ID) is shown in the top-right when the operating account is selected, or in the MCC's Sub-account settings under the Account column. Same dash-stripping rule.
Configuration
All configuration is via environment variables. Nothing is baked into the package.
| Variable | Required | Purpose |
|---|---|---|
| GOOGLE_ADS_DEVELOPER_TOKEN | yes | Developer token issued to your MCC. |
| GOOGLE_ADS_CLIENT_ID | yes | OAuth 2.0 client ID (Desktop app). |
| GOOGLE_ADS_CLIENT_SECRET | yes | OAuth client secret. |
| GOOGLE_ADS_REFRESH_TOKEN | yes | Refresh token from --login. |
| GOOGLE_ADS_LOGIN_CUSTOMER_ID | yes | MCC customer ID (no dashes). |
| GOOGLE_ADS_CUSTOMER_ID | yes for write tools | Operating customer ID (no dashes). Defaults to LOGIN_CUSTOMER_ID. |
| GOOGLE_ADS_MCP_CONFIRM_WRITES | optional | false to skip confirm:true requirement on destructive ops. Default true. |
| GOOGLE_ADS_MCP_DRY_RUN | optional | true makes every mutate tool log + return success without calling the API. |
| GOOGLE_ADS_MCP_MAX_BUDGET_MICROS | optional | Soft ceiling on create_budget/update_budget. Default 100000000000 (≈ $100,000/day). |
| GOOGLE_ADS_MCP_LOG_LEVEL | optional | error, warn, info, debug. Default info. |
Security and credential handling
The MCP requires six credentials, listed in Configuration. Treat all six as sensitive. In particular, the refresh token is a long-lived bearer credential: anyone who holds all six values can call the Google Ads API against your account and spend real money up to your configured daily budgets.
What not to do
Do not place any of the six credentials in:
- A git repository, public or private. Commit a
.env.examplewith empty values instead, and keep the real file out of source control. - A cloud-synced folder such as OneDrive, Dropbox, Google Drive, iCloud Drive, or Box. Anything in those folders syncs to every device your account is logged in on, plus the provider's servers and backups.
- A shared team workspace, ticket tracker, chat thread, support email, or AI-assistant transcript.
- A file readable by other user accounts on the same machine. On POSIX systems,
chmod 600the file before writing anything sensitive into it. On Windows, remove inherited permissions and grant your own user account only.
Recommended patterns
In rough order from most secure to acceptable:
- OS secret manager. Store each value in your operating system's keyring (Windows Credential Manager, macOS Keychain, Linux Secret Service /
gnome-keyring) and load them into environment variables at the moment you launch your MCP host. A small wrapper script that reads from the keyring and execs the host process is enough. - A
.envfile outside any synced folder. Keep the six values in a plain.envfile at a path that is not inside OneDrive, Dropbox, Drive, or similar, with file permissions restricted to your user. Load it via your MCP host's env-file support, or source it from your shell before launching the host. - Inline in the MCP host's JSON config. This is the simplest pattern and is what the MCP host config example below shows. It is acceptable only if the host config file lives on a local-only path with user-only permissions, and you understand that any backup tool, sync client, or screen-share pointed at that file will carry the secrets along with it.
A note on --login --save
The --login --save flow described in step 6 of One-time setup writes the refresh token to a per-user file at mode 0600 in your local app-data directory. This is meaningfully safer for the refresh token specifically than putting it into the MCP host's JSON config, because the file lives outside the host config's backup or sync surface and is readable only by your user account.
If you take this path, omit GOOGLE_ADS_REFRESH_TOKEN from the host config env block entirely. The server falls back to the saved file. The other five credentials still need to be provided as environment variables by one of the methods above.
If a credential leaks
Act quickly and in this order:
- Refresh token. Open https://myaccount.google.com/permissions, find your OAuth client (it appears under the app name you set on the consent screen), and click Remove Access. The next API call using the leaked token returns
invalid_grant. Then re-mint a new refresh token with--login. - OAuth client secret. In the Google Cloud Console: APIs & Services → Credentials → your Desktop OAuth client → Reset secret. The old secret stops working immediately. Update your config and re-mint the refresh token, because resetting the secret invalidates existing refresh tokens issued under that client.
- OAuth client ID. Treat the secret as also compromised. Delete the OAuth client, create a new one, update
GOOGLE_ADS_CLIENT_IDandGOOGLE_ADS_CLIENT_SECRET, and re-mint the refresh token. - Developer token. Developer tokens are issued by Google to your MCC and are not user-rotatable. If you believe the token has been leaked, contact Google Ads API support via your MCC's API Center page and request rotation. While you wait, reduce blast radius by lowering daily budgets on the affected operating customer accounts and by un-linking the operating account from the MCC if practical.
- Customer IDs. These are not secret on their own and are visible to anyone with access to the MCC or the sub-account. But if they leak together with the developer token and OAuth credentials, treat the whole bundle as compromised and rotate everything above.
Principle of least privilege
GOOGLE_ADS_CUSTOMER_ID scopes the MCP to a single operating customer account. Any tool call that passes a different customer_id is rejected at the client factory before reaching the API. Use this. Scope each MCP instance to the one operating account you accept it could spend against, and run a separate instance with a separate env block if you need to manage a second account. The scoping check is a backstop, not a substitute for choosing the target account deliberately.
MCP host config
Add to your MCP client's mcpServers block. The example below shows the six credentials as literal strings in JSON for clarity. Before pasting real values, read Security and credential handling above for what these credentials let an attacker do and for safer alternatives to inline secrets.
{
"mcpServers": {
"google-ads": {
"command": "npx",
"args": ["-y", "google-ads-full-readwrite-mcp"],
"env": {
"GOOGLE_ADS_DEVELOPER_TOKEN": "...",
"GOOGLE_ADS_CLIENT_ID": "...",
"GOOGLE_ADS_CLIENT_SECRET": "...",
"GOOGLE_ADS_REFRESH_TOKEN": "...",
"GOOGLE_ADS_LOGIN_CUSTOMER_ID": "1234567890",
"GOOGLE_ADS_CUSTOMER_ID": "1234567890"
}
}
}
}Tools
Reporting
list_accessible_customers— customer IDs the OAuth principal can accessget_account_summary— name, currency, time zone, campaign/ad-group countssearch— arbitrary GAQL queryget_resource_fields— fields available on a GAQL resourceget_performance— convenience wrapper for typical reports
Campaigns
list_campaignscreate_search_campaigncreate_performance_max_campaignupdate_campaignpause_campaign/enable_campaign/remove_campaign
Ad groups
list_ad_groupscreate_ad_groupupdate_ad_grouppause_ad_group/enable_ad_group/remove_ad_group
Ads
list_adscreate_responsive_search_adupdate_responsive_search_ad(read-then-replace; the API replaces, not patches)pause_ad/enable_ad/remove_ad
Assets and Performance Max asset groups
create_text_assetcreate_image_asset(data or URL)create_sitelink_assetcreate_callout_assetcreate_structured_snippet_assetcreate_video_assetcreate_asset_groupattach_assets_to_asset_groupattach_assets_to_campaignlist_assets
Budgets and bidding
create_budget/update_budget/list_budgetscreate_bidding_strategy/update_bidding_strategy/list_bidding_strategies
Keywords
add_keywordsadd_negative_keywords(AD_GROUP / CAMPAIGN / ACCOUNT scope)update_keywordremove_keywordlist_keywords
Recommendations
list_recommendationsapply_recommendation(requiresconfirm:true)dismiss_recommendation
Conversion actions
list_conversion_actions(read-only in v0.1)
Safety
This MCP can spend money. Four guardrails are on by default:
- Default-paused. Every
create_*_campaign,create_ad_group, andcreate_*_adcreates entities inPAUSEDstatus. The LLM has to explicitly callenable_*to start spend. - Confirmation for destructive operations.
remove_campaign,remove_ad_group,remove_ad,remove_keyword, andapply_recommendationrequire an explicitconfirm: truein the input. Without it the handler returns a "confirmation required" response describing the planned action. - Budget ceiling.
create_budgetandupdate_budgetreject amounts outside[1,000,000 micros, GOOGLE_ADS_MCP_MAX_BUDGET_MICROS]. - Customer-ID scoping. If
GOOGLE_ADS_CUSTOMER_IDis set, tool calls that pass a differentcustomer_idare rejected at the client factory. This prevents a prompt-injection from redirecting writes to the wrong account.
A GOOGLE_ADS_MCP_DRY_RUN=true env var makes every mutating tool log what it would submit and return synthetic success without calling the API. Use it the first week.
Troubleshooting
12 UNIMPLEMENTED: GRPC target method can't be resolved.
Google retires Google Ads API versions roughly 14 months after release. If you see this error, the version of google-ads-api that your install pulled in is now targeting a retired Google Ads API release. Bump the dep:
npm install -g google-ads-full-readwrite-mcp@latestIf you install via npx -y (the recommended pattern), clear the npx cache so the next launch pulls the current version of this package:
npx clear-npx-cacheinvalid_grant or "auth tokens are no longer valid"
Your refresh token has been revoked. Common causes:
- 90+ days of inactivity on the refresh token.
- The Google account password was changed.
- Consent was revoked from https://myaccount.google.com/permissions.
- The Google Cloud project's OAuth client was deleted, or its consent screen was returned to Draft.
Fix: re-run --login to mint a new refresh token, and update the value in your MCP host config.
DEVELOPER_TOKEN_NOT_APPROVED / "developer token not approved for the manager"
The developer token cannot be used against the operating customer account. Three common causes:
- Your developer token is Test access only and you're calling against a production account.
- The operating customer account is not linked to the MCC that owns the developer token.
GOOGLE_ADS_LOGIN_CUSTOMER_IDis set to the wrong MCC.
Confirm in the MCC's Sub-account settings that the operating account appears as Active, and that GOOGLE_ADS_LOGIN_CUSTOMER_ID matches the MCC's ID exactly (no dashes).
"Access blocked: ... has not completed the Google verification process"
You hit Google's consent screen but your Google account is not on the OAuth client's test users list. Go back to APIs & Services → OAuth consent screen (or Audience tab in the newer layout) and add your account under Test users, then retry --login.
MetadataLookupWarning at startup
MetadataLookupWarning: received unexpected error = All promises were rejected code = UNKNOWNgoogle-auth-library probes the GCE metadata server as a fallback when running outside Google Cloud. The probe fails (because you're not on a GCE VM), the library moves on, and the refresh-token flow proceeds normally. Harmless.
What this does not do
- YouTube / Display / Demand Gen creative beyond what Performance Max needs
- Shopping campaigns or Merchant Center management
- Creation of new conversion actions (read-only in v0.1)
- Multi-account batch operations across unrelated MCCs in one call
- A web UI or persistent server mode — the only interface is stdio MCP
Network traffic
The server makes outbound HTTP calls only to Google Ads and Google OAuth endpoints (*.googleapis.com, accounts.google.com, oauth2.googleapis.com). There is no telemetry, error reporting, or update check. A "no-phone-home" test in the test suite asserts this contract.
License
MIT. See LICENSE.
