Score a Google Ads MCP setup before an agent touches budget
Seven yes/no questions. One point per yes. Unanswered counts as not in place.
Runs entirely in your browser. Nothing you enter is sent anywhere.
Why do AI agents need guardrails on ad spend?
Because a budget is a live commitment, and one tool call is enough to make it.
A budget change is not a draft
When an MCP server exposes a write tool, a model can raise a daily budget, re-enable a paused campaign or drop a negative in a single call, and Google starts spending against the new number within minutes. The question is not whether the model is smart. It is whether one bad call can be bounded and afterwards reconstructed. AdRails keeps caps in a policy file the tools cannot write, requires a single-use expiring plan_id for every commit, and fails closed on exposure increases when the guard heartbeat is stale. selftest: enterprise_tools
Agents fail in loops, not in single answers
A loop that reads a metric and writes a budget every few minutes can walk a campaign past any sane number while each individual step looks reasonable. Guardrails interrupt the loop in the cheap direction: a pause is always permitted, a proposal that lowers exposure is always allowed, and every mutating call — refused ones included — is appended to a hash-linked record. selftest: rules, auditchain
What are the seven controls, and what does each one prevent?
Each control blocks a specific failure. The check line tells you what to look for in any setup, including your own.
Reviewed plan with a dry-run diff
Prevents the first view of a change from being the live account. The setup diffs a committed desired state against live budget, status, end date, negatives and URL fields, hands back a plan, and writes nothing. How to check: ask to see the diff, then ask whether a write path exists that skips it.
Caps the agent's tools cannot edit
Prevents the agent from raising its own ceiling mid-session. How to check: find the file or store holding the caps, then confirm the credentials the tools run under cannot write to it.
Validate-only before every commit
Prevents a malformed or over-cap change from landing. Google checks a change without applying it; the setup should run that check first and treat a refusal as zero side effects. How to check: ask what happens when a proposed budget breaks a cap.
Human approval for the expensive direction
Prevents an autonomous loop from raising spend or switching a campaign back on. How to check: is approval bound to one specific plan and to an expiring signed token, and are forged, expired or reused tokens refused?
Tamper-evident audit log
Prevents arguments about whether something happened. How to check: hash-linked entries, a verify command that detects edited, deleted or reordered rows, and a head digest you can publish so later truncation shows.
One-step rollback
Prevents a slow manual repair. How to check: can you rebuild state from an earlier commit, and are caps, account IDs and campaign IDs held out of the revert so rollback cannot wipe your limits or delete negatives?
Scheduled pause-only guard
Prevents an overnight runaway while the agent is idle or wedged. How to check: it can pause and never enable, and a stale heartbeat fails closed on increases rather than open.
How does AdRails implement each of the seven controls?
AdRails is an open-source (Apache-2.0) MCP server that runs on your machine with your credentials. Every claim below is proven by a named selftest module.
Plan, then a separate apply
- Dry-run diff first. adsops_plan compares the committed desired-state file against the live account for budget, status, end date, negatives and URL fields, writes nothing, and returns a plan_id that adsops_apply consumes. selftest: enterprise_tools
- Validate-only by default. adsops_apply runs in validate-only mode unless commit=true, which also needs a clean committed state file and commit_policy.allow_commit. selftest: enterprise_tools
- Single-use plan_id. A plan that has been committed cannot be replayed, and a refused call has zero side effects. selftest: enterprise_tools
Caps, approvals and the record
- Caps in a founder-owned policy file. The rules engine reads JSON conditions from it; rules can pause, add negatives, step a budget within caps (default max 20% per step) or alert, and can never enable anything. selftest: rules
- Approvals are signed and expire. An approver gets HMAC-SHA256 approve/reject tokens (default 24 h); forged, expired, reused or cross-plan tokens are refused, the secret is never written to disk, and the queue file is 0600. Modes: off, exposure, all. selftest: approvals
- The audit chain is always on. Every mutating call, including refused ones, is appended as a SHA-256 hash-linked entry with secrets redacted; adsops_audit_verify detects edited, deleted or reordered entries and returns a head digest you can publish. selftest: auditchain
Rollback and the pause-only guard
- One-command rollback. Rebuild the desired state as of any earlier git commit; policy caps, account and campaign IDs are never reverted and negatives are never removed (add-only rail). Commit refs are argument-injection-safe. selftest: rollback
- Pause-only guard. adsops_guard pauses a campaign when a daily or lifetime cap is crossed; pausing is always permitted, enabling never happens, and exposure increases fail closed when the guard heartbeat is stale. selftest: rules, enterprise_tools
Roles, drift, fleet and supply chain
- Deny-by-default roles. Viewer, operator, approver, guard and admin, enforced per tool once enabled; a refused call has zero side effects. selftest: rbac, enterprise_tools
- Drift from Google's change history. 30 days of out-of-band edits — web UI, scripts, auto-applied recommendations — flagged with budget, status and bidding changes highlighted. selftest: change_history
- Aggregate fleet cap. Across accounts and campaigns, assuming platforms may deliver up to 2x the daily budget; proposals that lower exposure are always allowed. selftest: fleet
- Verifiable code. A CycloneDX 1.5 SBOM plus a SHA-256 manifest of every package file, so you can check the code you run is the code released. selftest: sbom
What can no guardrail prevent?
Two things sit outside any local checklist, and both matter when you size exposure.
Google can deliver up to 2x the daily budget
AdRails does not bound what Google delivers. The aggregate fleet cap assumes the platform may deliver up to twice the daily budget, so the number in your policy file is a ceiling on commitments, not a promise about delivery. Use it to size exposure honestly rather than to assume a hard stop. selftest: fleet
An agent with shell access as the same OS user can bypass local rails
AdRails runs on your machine, so an agent that can execute commands as the same operating-system user can reach the same files and credentials. Unless you run the hardened install with a separate OS user, that bypass is available. The privilege-boundary item is still open in the threat model, and it should shape how much write access you hand out.
A guardrail bounds damage and makes it reconstructable. It does not make a weak strategy strong, and it does not replace reviewing what the agent proposed.
Which Google Ads MCP servers have which controls?
Check each vendor's docs, and re-check them, because write flows change.
Start with what a vendor states today
Google's official google-ads-mcp does read-only GAQL plus a propose-and-confirm write flow limited to assets and labels, vendor-stated as of 2026-11-05 and subject to change. That is a real control — the write is proposed and confirmed with a checksum rather than issued directly — but it is not a statement about budget writes.
Ask the same seven questions of any server
Where is the diff? Where do caps live and who can write them? Is there a validate-only step before commit? Who approves an increase? Can you verify the log? Can you roll back in one step? Does a schedule pause spend without any path to enable it? Vendor pages rarely answer all seven, so ask for the documentation and test on a low-budget account. Side-by-side notes live on the comparison page.
What does a score of 4 out of 7 look like in practice?
A plausible partial setup, and the two cheapest fixes.
The score
A team answers yes to questions 1, 2, 3 and 5: changes go through a plan, caps sit in a file the tools cannot write, validate-only runs before commit, and the audit chain is verified weekly. They answer no to 4 (no approval queue), 6 (rollback is manual) and 7 (no scheduled guard). That is 4 of 7 — the partial band.
What to fix first
Rollback is the cheapest win: rebuilding desired state from an earlier commit takes one command, and caps, account and campaign IDs are never reverted while negatives are add-only, so the recovery step cannot wipe its own limits. selftest: rollback The scheduled pause-only guard is second: it bounds a spend runaway while nobody is watching, and failing closed on exposure increases when the heartbeat is stale is a config change, not a migration. selftest: rules
Approval does not have to gate everything. With modes off, exposure or all, you can approve only the changes that raise spend and let pauses and negative additions run unattended.
Where do you go from here?
Read the control docs, check the rails in code, then score your own setup again after any config change.
For humans
- Guardrail reference. The rails in detail, including what is off by default and how to turn it on: /docs/guardrails/.
- Enterprise controls. RBAC, approvals, audit chain, rollback, drift and SBOM on one page: /enterprise/.
- Everything the server exposes. All 82 tools with their categories and which ones mutate: /docs/tools/.
For the agent in the loop
- adsops_whoami. Reports the server's identity and controls: version, principal and roles, approval mode, audit chain path, guard heartbeat. The closest tool equivalent of this checklist.
- adsops_audit_verify. Walks the hash-linked chain and returns ok, the entry count, the first bad entry if any, and a head digest to publish.
- Source and selftests. Run
python3 -m adsops --selftest— 2,704 checks, all passing on 2026-11-06. Code at github.com/antonioac1/adrails.
Straight answers.
Can an AI agent change Google Ads budgets on its own?
Only if you hand it a tool that writes. An MCP server exposes tools, and a budget call reaches the live account. In AdRails every change starts from a committed desired-state file, goes through adsops_plan for a dry-run diff, then adsops_apply in validate-only mode before a single-use plan_id commits it. Approval mode can gate exposure increases to a human.
What is validate-only mode in Google Ads?
Validate-only asks Google to check a change without applying it, so a malformed field, a missing resource or a policy violation comes back as an error rather than a live edit. AdRails runs validate-only before every commit and treats a refused call as having zero side effects, which is what makes a dry-run diff trustworthy.
Why must spending caps live outside the agent's tools?
A cap the agent can edit is not a cap; it is a suggestion the model can revise in the same session. AdRails keeps caps in a founder-owned policy file the tools cannot write, and its fleet cap assumes platforms may deliver up to 2x the daily budget while proposals that lower exposure are always allowed.
Do guardrails stop Google overspending a daily budget?
No. Google can deliver up to 2x the daily budget, and AdRails does not bound what Google delivers — its aggregate cap assumes that multiplier so you size exposure honestly. Guardrails bound what the agent can commit, keep a tamper-evident record of every mutating call, and give you one-command rollback.
How does AdRails compare with Google's official Google Ads MCP server?
They overlap but differ. Google's official server does read-only GAQL plus a propose-and-confirm write flow limited to assets and labels, vendor-stated as of 2026-11-05. AdRails is an open-source (Apache-2.0) MCP server that runs on your machine with your credentials and routes budget changes through plan, apply and commit. Check each vendor's docs for current coverage.
What score should a production Google Ads MCP setup reach?
Six or seven. Anything below six leaves at least one cheap control missing — usually rollback or a scheduled pause-only guard — and the 3–5 band means the setup can move spend without a complete safety net. A score of 0–2 means a single tool call can commit budget with no plan, no uneditable cap and no verifiable record.