Google Ads MCP Admin
# Google Ads MCP Admin
An extension of Google's [official Google Ads MCP](https://github.com/googleads/google-ads-mcp) that adds confirmed campaign administration across client accounts managed by a Google Ads manager account.
Website: [jomiferse.com](https://jomiferse.com)
Read tools run directly. Every persistent write requires an immutable plan, a preview, interactive approval, and post-write verification. High-risk changes require two independent approvals.
## Safety model
- Mutation plans are immutable, expire after 15 minutes, and can be applied only once.
- The server revalidates the client hierarchy immediately before a write.
- Normal writes require approval for `mutations_apply_mutation_plan`.
- High-risk writes first require `mutations_acknowledge_high_risk_plan`, followed by a separate apply approval.
- Ambiguous API responses are never retried automatically.
- Audit records contain metadata and request IDs, not credentials or complete payloads.
## Prerequisites
- macOS, Windows, or Linux.
- Python 3.11 or later.
- [`uv`](https://docs.astral.sh/uv/).
- Google Cloud CLI (`gcloud`).
- A Google Cloud project with the Google Ads API enabled.
- A Desktop OAuth client authorized for a Google user who can access the target manager account.
- An approved Google Ads API developer token.
Install the pinned dependencies:
```bash
uv sync --extra dev
```
## Configure Codex
Copy the public configuration template and replace its `cwd` value with the absolute path to this repository:
```bash
cp .codex/config.toml.example .codex/config.toml
```
The local `.codex/config.toml` file is ignored by Git. Provide these non-secret variables to the Codex process. POSIX shell:
```bash
export GOOGLE_CLOUD_PROJECT="your-google-cloud-project"
export GOOGLE_ADS_LOGIN_CUSTOMER_ID="1234567890"
```
PowerShell:
```powershell
$env:GOOGLE_CLOUD_PROJECT = "your-google-cloud-project"
$env:GOOGLE_ADS_LOGIN_CUSTOMER_ID = "1234567890"
```
Google Ads account IDs must contain digits only, without hyphens. `GOOGLE_APPLICATION_CREDENTIALS` is optional when Application Default Credentials are stored in the standard gcloud location.
You may place non-secret local values in the ignored configuration instead:
```toml
[mcp_servers.google_ads_admin.env]
GOOGLE_CLOUD_PROJECT = "your-google-cloud-project"
GOOGLE_ADS_LOGIN_CUSTOMER_ID = "1234567890"
```
Never place the developer token, OAuth client secret, or ADC refresh token in this repository.
## Configure OAuth
Create a Desktop OAuth client in Google Auth Platform and download its JSON file outside the repository. On macOS or Linux, run the optional helper:
```bash
export GOOGLE_CLOUD_PROJECT="your-google-cloud-project"
scripts/configure-google-ads-oauth.sh "/absolute/path/to/oauth-client.json"
```
On Windows PowerShell, run the equivalent Google Cloud CLI commands:
```powershell
$env:GOOGLE_CLOUD_PROJECT = "your-google-cloud-project"
gcloud services enable googleads.googleapis.com --project $env:GOOGLE_CLOUD_PROJECT
gcloud auth application-default login `
--scopes="https://www.googleapis.com/auth/adwords,https://www.googleapis.com/auth/cloud-platform" `
--client-id-file="C:\absolute\path\to\oauth-client.json"
```
The helper enables `googleads.googleapis.com` and requests only these scopes:
- `https://www.googleapis.com/auth/adwords`
- `https://www.googleapis.com/auth/cloud-platform`, required for Application Default Credentials
The resulting ADC file is stored outside the repository by gcloud.
## Store the developer token
Store or replace the token with a hidden prompt:
```bash
uv run google-ads-mcp-admin-credentials set
```
The command uses the native credential store selected by Python [`keyring`](https://keyring.readthedocs.io/en/latest/):
- macOS Keychain.
- Windows Credential Locker.
- Linux Secret Service or KWallet when a compatible backend is available.
Check which source is active without displaying the token:
```bash
uv run google-ads-mcp-admin-credentials status
```
Delete only the native credential with:
```bash
uv run google-ads-mcp-admin-credentials delete
```
On desktop Linux, install the Secret Service or KWallet packages recommended by your distribution and run the application in an active D-Bus session. `keyring diagnose` reports the selected backend. Headless Linux, CI, and containers can use the environment fallback instead.
POSIX shell fallback:
```bash
export GOOGLE_ADS_DEVELOPER_TOKEN="your-developer-token"
```
PowerShell fallback:
```powershell
$env:GOOGLE_ADS_DEVELOPER_TOKEN = "your-developer-token"
```
The native credential has priority when both sources are configured. Never place the token in `.codex/config.toml`, `.env`, command arguments, or this repository.
### Migration from the macOS-only release
Run `uv run google-ads-mcp-admin-credentials set` and paste the existing token into the hidden prompt. After `status` reports `native`, remove the legacy `codex-google-ads-developer-token` item with the Keychain Access application. Automatic migration is intentionally not performed.
## Start the MCP server
Set the manager account and start the server manually to verify startup:
```bash
export GOOGLE_ADS_LOGIN_CUSTOMER_ID="1234567890"
uv run google-ads-mcp-admin
```
PowerShell:
```powershell
$env:GOOGLE_ADS_LOGIN_CUSTOMER_ID = "1234567890"
uv run google-ads-mcp-admin
```
After editing `.codex/config.toml`:
1. Restart Codex or reopen the project.
2. Open `/mcp` and confirm that `google_ads_admin` is available.
3. Confirm that the official read tools and the five `mutations_*` tools are present.
Do not weaken the write approval policies when the server can access production accounts.
## Read-only verification
Offline checks require no credentials:
```bash
uv run pytest -m "not integration" -q
```
After OAuth and credential setup, run the opt-in live read test. The test resolves the native token first and uses the environment fallback when needed:
```bash
export GOOGLE_ADS_LOGIN_CUSTOMER_ID="1234567890"
GOOGLE_ADS_RUN_INTEGRATION=1 \
uv run pytest tests/integration/test_google_ads_read.py -v
```
The test discovers enabled, non-manager client accounts and queries one of them without writing.
## Mutation workflow
1. Call `mutations_plan_mutations` with a ten-digit `customer_id` and structured operations.
2. Review `preview`, `old_values`, `new_values`, `warnings`, `risk`, `plan_id`, `plan_hash`, and expiration.
3. Approve `mutations_apply_mutation_plan` in Codex for a normal-risk plan.
4. The server revalidates manager access, claims the plan to prevent replay, dispatches it once, and reads the resulting resources.
5. Review `request_ids` and `verification`.
Use `mutations_cancel_mutation_plan` with the plan ID and hash to invalidate a pending plan. Plans are held only in memory and disappear when the server restarts.
## High-risk changes
The following operations are high risk:
- Enabling a campaign.
- Creating a budget.
- Increasing a budget by more than 25 percent.
- Removing ten or more resources in one batch.
First approve `mutations_acknowledge_high_risk_plan`. Codex then requests a separate approval for `mutations_apply_mutation_plan`. Neither call can replace or alter the saved operations.
## Supported resources
- `campaign_budget`
- `campaign`
- `ad_group`
- `ad_group_ad`
- `ad_group_criterion`, including keywords
- `campaign_criterion`, including targeting
Supported actions are `create`, `update` with an explicit `update_mask`, and `remove`. Google Ads removal commonly changes status to `REMOVED`; it is not physical deletion and may be irreversible.
## First persistent write
Complete these checks before any persistent write:
1. `uv run ruff check .`
2. `uv run pytest -m "not integration" -q`
3. The live read-only integration test.
4. `validate_only` against a dedicated test account:
```bash
export GOOGLE_ADS_TEST_CUSTOMER_ID="1234567890"
GOOGLE_ADS_RUN_INTEGRATION=1 \
uv run pytest tests/integration/test_google_ads_validate_only.py -v
```
The first persistent test should create a `PAUSED` campaign with the minimum practical budget in a test account. Review and approve its plan, verify the resources, then create and approve a separate removal plan.
If no test account is available, stop after `validate_only`. A first write to production requires separate explicit authorization and should be limited to one paused resource with minimal financial impact.
## Recovery and auditing
- Expired or cancelled plan: create a new plan; it cannot be reactivated.
- Revoked OAuth grant: rerun `scripts/configure-google-ads-oauth.sh`.
- Replaced developer token: run `uv run google-ads-mcp-admin-credentials set` again.
- Ambiguous write response: do not retry; query state once and preserve the returned request ID.
- Quota, permission, or policy error: preserve the Google Ads request ID for diagnosis.
- Client removed from the manager hierarchy: the pre-write revalidation rejects the operation.
Local audit events are written to `audit/google_ads_mcp.jsonl`, rotate at 5 MiB, and contain account, plan, resource counts, result, and request IDs. They do not contain complete mutation payloads or credentials.
## Development
```bash
uv run ruff check .
uv run pytest -m "not integration" -v
uv build
git diff --check
```
The official server dependency is pinned to upstream commit `f48a6b85e1f43ebd44a72531c9611e2b7265ca28`. Review upstream changes, rerun all checks, and inspect MCP tool annotations before updating it.
## License
Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE).
TDQS
Scored across 8 tools
Each tool serves a distinct purpose: customer discovery, metadata lookup, data search, and mutation plan lifecycle. The only mild ambiguity is between 'plan_mutations' and 'get_mutation_plan', as both return previews, but their roles (create vs retrieve) are clarified in descriptions.
Tools follow a consistent [domain]_[action] pattern with snake_case, such as customers_list_accessible_customers and mutations_apply_mutation_plan. The name 'search_search' is slightly awkward (resource and verb identical), but overall the pattern is predictable and coherent.
Eight tools is well-scoped for the Google Ads admin domain, covering customer access, metadata/field discovery, search, and a complete mutation planning workflow. No redundant or excessive tools.
The mutation plan lifecycle is well covered (plan, get, cancel, acknowledge, apply), and search is supported by metadata discovery. Minor gaps include no way to list all mutation plans and no single-customer detail endpoint, but these are edge cases rather than core dead ends.