A local Model Context Protocol server that
lets Claude (and other MCP clients) read your Google Ads data and manage
campaigns. Built with Python + FastMCP + Google's official google-ads client,
running over stdio.
⏳ Heads-up before you start: a fresh Google Ads developer token is test-only. To query/modify real accounts you must apply for Basic Access, which typically takes 1–3 business days to approve. Apply early (step 4 below) and develop against a test account meanwhile.
Read-only (always available)
| Tool | What it does |
|---|---|
list_accessible_customers |
List customer (account) IDs your credentials can reach. |
run_gaql |
Run any GAQL query — the main reporting tool. Paginated + row-capped. |
get_resource_fields |
List valid fields/metrics/segments for a resource (schema grounding). |
get_campaign_performance |
Quick campaign summary (impressions, clicks, cost, conversions). |
Write (guarded — only when GOOGLE_ADS_ALLOW_MUTATIONS is enabled; default on)
| Tool | What it does |
|---|---|
update_campaign_budget |
Set a campaign budget's daily amount. |
update_campaign_status |
Pause / enable / remove a campaign. |
update_ad_group_status |
Pause / enable / remove an ad group. |
set_keyword_bid |
Adjust a keyword's max CPC bid and/or status. |
mutate |
Advanced escape hatch: arbitrary GoogleAdsService.Mutate operations. |
Every write tool defaults to a DRY RUN (Google Ads validate_only): it
checks the request and changes nothing unless you pass apply=true. Set
GOOGLE_ADS_ALLOW_MUTATIONS=false to hide all write tools entirely.
uv(manages Python + dependencies). It will fetch Python 3.12 automatically (pinned in.python-version).- A Google Ads account, ideally with a Manager (MCC) account.
cd /Users/fiveze/Projects/googleadsmcp
uv sync # creates .venv and installs mcp[cli], google-ads, google-auth-oauthlibYou need five values, all supplied as environment variables.
- Create a project at https://console.cloud.google.com.
- APIs & Services → Library → enable Google Ads API.
- APIs & Services → OAuth consent screen. User type External is fine; you can leave it in Testing.
- Add your Google account under Test users.
- Scope used by this server:
https://www.googleapis.com/auth/adwords.
- APIs & Services → Credentials → Create credentials → OAuth client ID.
- Application type: Desktop app.
- Copy the Client ID and Client secret (or download the JSON).
- In your Google Ads Manager (MCC) account: Tools → API Center.
- Copy the developer token. Apply for Basic Access now — a fresh token only
works against test accounts (
DEVELOPER_TOKEN_NOT_APPROVEDotherwise) and approval takes ~1–3 business days.
With CLIENT_ID/CLIENT_SECRET exported (or a client_secret.json downloaded):
export GOOGLE_ADS_CLIENT_ID=... # or pass the json path as an arg
export GOOGLE_ADS_CLIENT_SECRET=...
uv run scripts/generate_refresh_token.py
# (or) uv run scripts/generate_refresh_token.py /path/to/client_secret.jsonA browser opens for consent; the script prints GOOGLE_ADS_REFRESH_TOKEN=....
Your Manager (MCC) account ID, digits only (no dashes). Required when the target account is managed by an MCC.
cp .env.example .env # then fill in the five values.env holds account-wide secrets — it is git-ignored; keep it that way.
A project-scoped .mcp.json is already included. It uses ${VAR} expansion, so
no secrets are committed — export the values (e.g. source .env) in the shell
that launches Claude Code, then approve the server when prompted. Verify with
/mcp inside a session, or:
claude mcp listPrefer the CLI / user scope instead? With your env exported:
claude mcp add google-ads --scope user --transport stdio \
--env GOOGLE_ADS_DEVELOPER_TOKEN=$GOOGLE_ADS_DEVELOPER_TOKEN \
--env GOOGLE_ADS_CLIENT_ID=$GOOGLE_ADS_CLIENT_ID \
--env GOOGLE_ADS_CLIENT_SECRET=$GOOGLE_ADS_CLIENT_SECRET \
--env GOOGLE_ADS_REFRESH_TOKEN=$GOOGLE_ADS_REFRESH_TOKEN \
--env GOOGLE_ADS_LOGIN_CUSTOMER_ID=$GOOGLE_ADS_LOGIN_CUSTOMER_ID \
-- uv run --directory /Users/fiveze/Projects/googleadsmcp server.pyEdit ~/Library/Application Support/Claude/claude_desktop_config.json and add
(literal values — Claude Desktop does not expand ${VAR}):
{
"mcpServers": {
"google-ads": {
"command": "uv",
"args": ["run", "--directory", "/Users/fiveze/Projects/googleadsmcp", "server.py"],
"env": {
"GOOGLE_ADS_DEVELOPER_TOKEN": "...",
"GOOGLE_ADS_CLIENT_ID": "...",
"GOOGLE_ADS_CLIENT_SECRET": "...",
"GOOGLE_ADS_REFRESH_TOKEN": "...",
"GOOGLE_ADS_LOGIN_CUSTOMER_ID": "1234567890"
}
}
}
}Then fully restart Claude Desktop. That file now holds real secrets — chmod 600
it and don't sync it.
source .env
uv run mcp dev server.pyIn the Inspector: confirm the tools list, then call list_accessible_customers
(validates auth) and run_gaql with
SELECT customer.id, customer.descriptive_name FROM customer.
For writes, call e.g. update_campaign_budget with the default (dry run) first —
it should report "Dry run OK" with no change — then apply=true on a low-risk
test campaign.
| Env var | Required | Notes |
|---|---|---|
GOOGLE_ADS_DEVELOPER_TOKEN |
yes | From MCC API Center. |
GOOGLE_ADS_CLIENT_ID |
yes | OAuth2 desktop client. |
GOOGLE_ADS_CLIENT_SECRET |
yes | OAuth2 desktop client. |
GOOGLE_ADS_REFRESH_TOKEN |
yes | From generate_refresh_token.py. |
GOOGLE_ADS_LOGIN_CUSTOMER_ID |
if MCC | Manager account ID, digits only. |
GOOGLE_ADS_API_VERSION |
no | Pin an API version (e.g. v24); else client default. |
GOOGLE_ADS_ALLOW_MUTATIONS |
no | false hides all write tools. Default enabled. |
- Money is micros (÷ 1,000,000). Bids/budgets are set in micros.
- GAQL field names: use
get_resource_fieldsto avoid invalid-field errors. - API version churn: Google sunsets versions periodically — bump
GOOGLE_ADS_API_VERSION(or upgradegoogle-ads) if you hitUNSUPPORTED_VERSION. - Safety: write tools are dry-run by default and marked destructive; keep a
human in the loop before
apply=true.
This server is provided as-is for personal/local use.