Google shipped an official Model Context Protocol server for Google Ads, and its documentation is refreshingly direct about the limitation: it is strictly read-only. It cannot modify bids, pause campaigns, or create new assets. An agent connected to it can tell you what is happening in your account, but it cannot do anything about it.
We understand the instinct. Writes to a live ad account move real money, and the failure mode of an over-eager agent is a maxed-out card, not a bad paragraph. But read-only is not a safety model. It is the absence of one. The interesting engineering problem is letting an agent change things without handing it unsupervised spend, and that is the problem we built our execution protocol around.
The Protocol: Every Write Is a Dry Run Until You Say Otherwise
The Synter MCP server exposes a universal execute tool that can run any whitelisted action: create campaigns, change budgets, pause spend, add keywords, generate creatives. The rule that makes this safe is simple:
dry_run defaults to true. The first call validates everything and executes nothing. Running the action for real requires explicitly passing dry_run=false on a second call.Here is a real first call, exactly as an agent would make it:
{
"action": "google_ads_create_search_campaign",
"platform": "GOOGLE",
"args": [
"--name", "Brand Defense - Jul 2026",
"--daily-budget", "25",
"--keywords", "your brand name,your brand pricing"
]
}And the response:
{
"success": true,
"dry_run": true,
"action": "google_ads_create_search_campaign",
"platform": "GOOGLE",
"message": "Dry run only - NOTHING was executed. The action is
whitelisted and its arguments passed validation.",
"next_step": "Re-call execute with dry_run=false to actually
run this action.",
"connection_resolved": true,
"credit_cost": 5
}Four things happened before that response came back, and none of them touched the ad account: the action was checked against the whitelist, the arguments were validated against the script's actual CLI contract, the platform credentials were resolved to confirm the connected account can even perform this action, and the credit cost was computed. If any of those fail, the agent finds out now, on a call that cannot spend anything.
Agents Learn the Protocol at Runtime
Look at the next_step field in that response again. That line is the most important design decision in the whole system:
"next_step": "Re-call execute with dry_run=false to actually run this action."An agent does not need to have read our documentation to use the protocol correctly. The gate teaches it. Any MCP client, whether it is Claude, ChatGPT, Codex, or a homegrown runner, hits the dry-run wall on its first write attempt and is told, in the response payload, exactly what the deliberate next step is. The protocol is self-describing at the exact moment it matters.
When the agent and its human are ready, the second call is one field away:
{
"action": "google_ads_create_search_campaign",
"platform": "GOOGLE",
"args": [
"--name", "Brand Defense - Jul 2026",
"--daily-budget", "25",
"--keywords", "your brand name,your brand pricing"
],
"dry_run": false
}That call runs the action for real. For anything that spends money, the expectation is that the flip from validation to execution happens with the account owner's approval, and the two-call shape gives the agent a natural place to pause and ask.
Richer Previews: Let the Script Simulate
There is a second layer for actions where you want more than validation. Many of the underlying scripts accept their own --dry-run flag. When an agent passes it in args, the gate steps aside and lets the script itself simulate, which returns a much richer preview than the generic validation receipt:
{
"action": "reddit_ads_update_campaign",
"platform": "REDDIT",
"args": ["--campaign-id", "253819...", "--action", "pause", "--dry-run"],
"dry_run": false
}Validation-level dry runs answer “is this call well-formed?” Script-level dry runs answer “what exactly would change?” An agent can walk the ladder: validate, simulate, then execute.
Side by Side
| Official Google Ads MCP | Synter MCP | |
|---|---|---|
| Read access | Google Ads only | 19 ad platforms |
| Write access | None. Documented as strictly read-only | Campaign creation on 14 platforms; write actions on 16 |
| Safety model | Not applicable, nothing can change | Every write dry-runs by default; execution is an explicit opt-in |
| Agent guidance | Not applicable | The dry-run response tells the agent exactly how to proceed |
| Creative generation | No | Images and video via generation models |
| Audience sync | No | Google, Meta, LinkedIn, Microsoft, Reddit, TikTok, X |
The platform counts are current as of July 2026: reporting across 19 ad platforms, full campaign creation on 14 of them, including Google Ads, Microsoft, Meta, LinkedIn, X, Reddit, TikTok, Amazon Ads, and OpenAI Ads, plus write actions without campaign creation on DV360 and StackAdapt.
Why Read-Only Is the Wrong Ceiling
A read-only server caps the value of an agent at “analyst.” It can find the campaign that burned budget overnight, and then a human has to open the dashboard and click the pause button themselves. The finding was automated; the fix was not. That gap is where most of the time still goes.
The dry-run protocol closes the gap without removing the human. The agent proposes with a validated, costed, executable call. The human approves. The same call runs with one field changed. Nothing about that flow required making writes impossible; it required making them deliberate.
npx @synterai/mcp-server. Connect an ad account, ask your agent to propose a change, and watch it hit the dry-run gate before anything moves. Create a Synter account to get an API key, or read the agent execution docs.