
AdRails
dev.adrailsPublished by adrails.devv0.8.0Updated Nov 7, 2026
Your AI runs lead gen: Google Ads, LSA, ChatGPT Ads, AEO, Business Profile, playbooks, guardrails
The publisher's own server: Adrails
Overview
Self-hosted Google Ads MCP server that lets an AI agent read campaigns and propose capped, reviewed ad changes on your own credentials.
- What you can do
- Read campaigns, live status, spend, clicked search terms and change history
- Diff a desired-state file against live state, validate-only, then commit with a single-use plan ID
- Propose budget, status, end-date and negative-keyword changes within policy caps, plus pause-only spend guards
- Audit and diagnose: conversion tracking score, wasted spend, landing-page checks, RSA validation, drift and rollback
- Report across campaigns and accounts: monitors, briefs, fleet view, budget reallocation suggestions, competitor intel
- Run local-business playbooks and AEO checks (crawler access, llms.txt, AI visibility), plus direct-mail tracking
- When to use it
- Use it when you want an AI assistant to help operate a Google Ads account without giving it unchecked write access: reviewing spend and search terms, drafting capped budget or negative-keyword changes, auditing conversion tracking, or running a 90-day local-business plan. It is aimed at owners and marketers who can host it locally and keep state files in git.
- Requirements
- Runs locally over stdio as a Python package (pip install adrails, console script adsops-mcp); needs Python and a config JSON file. Requires your own Google Cloud project and Google Ads credentials (service-account JSON file, static token, or secret-manager lookup) plus a developer token for some tools. Optional OpenAI Ads API key per ad account for ChatGPT Ads tools. A git-tracked state file and policy file per campaign, and an OS scheduler for the spend guard.
Installation
In SourceWeft
- Open AdRails in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Other MCP clients
Follow the launch instructions in the repository.
README
AdRails (adrails.dev)
Guardrails for AI agents running your ads: an open-source, self-hosted Google Ads MCP server.
Website and docs: https://adrails.dev · MCP Registry: dev.adrails/adrails · plain-text summary for agents: https://adrails.dev/llms.txt
AdRails is a local MCP server for Google Ads. An agent may read campaigns, status and search
terms as often as it likes; every write has to land in a reviewed desired-state file on disk,
get diffed against live state, and clear caps that live in a policy file no AdRails tool writes.
The Python package and module are still named adsops — that is the name you install and run.
Three things it does differently:
- Your keys. It runs on your machine against your own Google Cloud project and Google Ads credentials. Nothing is hosted: the only endpoint the package talks to is the Google Ads API, and credentials are read from the sources you configure (a service-account JSON file, a static token, or a secret-manager lookup).
- Your caps. Caps live in a policy file that no AdRails tool writes; with
require_plan_idon, every commit must quote a fresh, single-useplan_idissued for that exact plan; and a change that adds spend exposure is refused while the scheduled guard's heartbeat is stale. - Pay per call in sats. Optional hosted add-ons, Lightning-first, metered per call in sats — coming soon. Nothing in this release contacts any hosted service.
New in 0.8.0: a full local-business suite your AI runs after one intake interview. Five playbooks (AEO, local leads, Google Business Profile, Google Ads, ChatGPT Ads), each 90-day plan task linked to the section behind it,
adsops_resultsbefore/after reports and privacy-safe case studies,adsops_gbpto get a Business Profile to 100, and amonthlyprompt. Plus update awareness (adsops_update_check,adsops-mcp upgrade). 82 tools.
Common questions
What is the safest way to let an AI agent manage Google Ads?
Give the agent a write path that cannot exceed limits it doesn't control. With AdRails, every change is a git-committed desired-state file, then adsops_plan (a dry-run diff), then Google's validate-only check, then a commit with a single-use plan ID. Caps live in a policy file no AdRails tool can write, and an hourly guard can only pause. Checklist: Google Ads MCP safety: 7 controls.
Is there an open-source Google Ads MCP server that runs locally?
Yes. AdRails is Apache-2.0, runs on your machine over stdio with your own Google Cloud project and keys, and installs with pip install adrails. Nothing is hosted.
How do I connect Claude Code, Cursor or Grok to Google Ads?
pip install adrails, then adrails init --dir DIR --customer-id ID --campaign-id N --sa KEY.json and adrails doctor, then claude mcp add adrails -- adrails --config DIR/config.json (Cursor and Grok use the same mcpServers entry). See install guides.
Can I require human approval before an AI agent changes my ad budget?
Yes. Set approvals.mode to exposure (budget increases, enabling, end-date extensions) or all. The agent calls adsops_approval_request, a human approves with an HMAC-signed token that expires, and a commit without an approved plan_id is refused.
How do I audit every change an AI agent makes to my ad accounts?
Every mutating call, including refused ones, is appended to a SHA-256 hash-chained log with secrets redacted. adsops_audit_verify detects edited, deleted or reordered entries. adsops_rollback rebuilds any earlier committed state, and adsops_drift flags edits made outside AdRails.
How do I check whether Google Ads conversion tracking actually works?
adsops_conv_audit scores the account 0-100: no primary goal, auto-tagging off, primary actions with zero conversions, double counting, lead actions counting every click, primary actions whose goal is not biddable, spend without conversions, and smart bidding without enough data.
How can an agent check if ChatGPT, Claude and Perplexity can crawl and cite my site?
aeo_site_audit checks robots.txt rules for 18 AI and search crawlers (RFC 9309), llms.txt, the sitemap, noindex and canonical tags, and whether the body text is readable without JavaScript. Free browser version: AI crawler checker.
How do I validate or build an llms.txt file?
aeo_llms_txt validates a live /llms.txt against the llmstxt.org format, or builds one from a page list.
How do I track whether AI assistants recommend my business?
Your agent asks ChatGPT, Claude or Perplexity your category questions and passes the answers to aeo_visibility_record. It scores mentions, rank against competitors, citations and sentiment, and reports the change since the last run. AdRails makes no LLM calls itself.
How do I track response rates and cost per lead for postcards?
mail_codes_assign gives every piece a phone-readable tracking code and a UTM-tagged URL. mail_scans counts real scans from your own server logs (bots and your own test scans removed). mail_response reports rates with 95% intervals, and channel_cac puts direct mail cost per lead next to Google Ads. Free browser tools: postcard QR codes, response calculator, holdout calculator.
Does AdRails support ChatGPT Ads (the OpenAI Ads API)?
Not yet. The adapter is in development; it will use the same plan, validate and commit path, with OpenAI's Bulk API validate_only and partial_failure forced off.
Testing
The QA suites were written adversarially by a separate agent with no write access to adsops/, and every finding was fixed
or documented as intended behaviour.
What AdRails cannot guarantee
Read this before pointing it at a live account.
- It is a threshold with automatic pause attempts, not a hard spend ceiling. The guard compares spend with numbers you wrote and issues pause operations. That is containment, not billing control.
- Google can deliver up to 2x the average daily budget on a given day. A daily budget is an average, not a maximum, and a pause is an API call that can be rejected or land late.
- Reporting is delayed. The spend figures AdRails reads are minutes to hours behind delivery.
- The guard only acts when it is scheduled and the machine is awake. A chat-session MCP process is not a monitor: while the machine sleeps no checks run, spend keeps moving, and the heartbeat goes stale.
- An agent with shell access as the same user can bypass any local tool. AdRails constrains the tool calls it serves, not the machine underneath. Sandbox the agent if that matters to you.
- Wrong negative keywords can suppress good traffic. Negatives are append-only here, so a bad one is also awkward to withdraw.
- You are responsible for your account and your spend. Keep platform-side billing limits as the real ceiling.
Quickstart
1. Install.
No other dependencies. Service-account tokens are signed with your system openssl
when google-auth isn't installed (install google-auth if you prefer it).
The core is stdlib-only. Check it offline (everything mocked, no network):
2. Write a config. Every account, credential source and path lives here; paths are
~-expanded at load time.
commit_policy defaults to require_clean_git: true and allow_commit: false, so commits and
adsops_set stay off until you turn them on.
3. Write the policy file for the campaign (next section), and commit both files to git so
git diff is the change review.
4. Register the server in your MCP client. Diagnostics go to stderr; the server speaks JSON-RPC 2.0 over stdio, one object per line.
5. Schedule the guard. A chat session cannot watch spend, so the pause-only spend check runs
from your OS scheduler every 15 minutes. Templates for launchd, systemd and cron live in
deploy/; render the placeholders (__PYTHON__, __CONFIG__, __PKG__, __LOGDIR__) and read
deploy/GUARD.md before installing:
The command all three units run is PYTHONPATH=$PKG $PY -m adsops --config $CFG guard-all.
Use it from Grok, Claude Code, Cursor or any MCP client
AdRails is a local stdio server, so every client registers the same command. There is no hosted URL, by design.
Cursor or a project .mcp.json:
Claude Desktop: install the .mcpb bundle from the latest release.
More: https://adrails.dev/docs/mcp-clients/
Tools
New in 0.4.0: enterprise controls, automation and an AEO toolkit (28 tools)
Hardening applied to every tool (adsops/enterprise_tools.py:harden): role checks (rbac, off until enabled, then deny-by-default),
an approval gate on commits (approvals.mode: off | exposure | all; HMAC-signed, expiring tokens; secret from env
ADRAILS_APPROVAL_SECRET, 32+ chars), and a SHA-256 hash-chained audit log of every mutating call, refused ones included
(audit_chain, default ~/.adsops/audit-chain.jsonl). Monitors deliver to the webhook URL in the env var named by alerts.webhook_env.
Nothing here adds spending authority: account changes still go committed state file → adsops_plan → adsops_apply.
New CLI: adsops-mcp init --dir DIR --customer-id ID --campaign-id N --sa KEY.json scaffolds a campaign with a safe policy
(caps = today's budget, commits off, enabling forbidden) and makes the first git commit; adsops-mcp --config DIR/config.json doctor
reports what's missing, with a fix for each failed check.
Optional per-campaign config for the new tools: negatives (protect, out_of_area, competitor_brands, home_state, llm_command) and landing (towns); top-level intel_config points at your AdRails Intel tenant file (see adsops/intel/README.md).
The policy file
One policy file per campaign, referenced by policy_file in the config. AdRails only ever reads
it: no tool writes caps, and adsops_set refuses guards and allow_enable outright. If the
state file also carries caps they are ignored — the policy file always wins, and adsops_status
says so.
A missing or malformed field is an error that names the field, not a silent default.
Desired-state file
One JSON file per campaign, referenced by state_file. It says what the campaign should look
like; the server computes the diff.
Keep these files in git. That is what makes "reviewed desired state" real: git diff is the
change review, and require_clean_git makes the server refuse to act on uncommitted edits.
ChatGPT Ads
AdRails also runs OpenAI (ChatGPT) Ads accounts through the same plan, validate and commit path. The tools
are named adsops_openai_*. Full guide: docs/OPENAI-ADS.md.
- One API key per ad account. It comes from 1Password, a file or an env var. Any number of
openai_adsaccounts can sit in one config. - New campaigns, ad groups and ads are always created paused. Each create carries an
Idempotency-Key, so a retry cannot duplicate it. - Caps live in an immutable policy file:
- max daily and lifetime budget
- max cumulative spend per campaign key, and optionally for the whole account
- max bid
- allowed destination domains and required UTM parameters
- no archiving unless allowed
- Activating a campaign needs a human approval block in the committed desired-state file. It is single-use and bound to the campaign name. Activation also needs a fresh guard heartbeat and room under the cap, assuming 2x daily overdelivery.
adsops_openai_guardonly pauses. It uses live budgets and bids, counts every campaign AdRails created for the key, and pauses an orphaned one. If spend can't be read, it fails closed instead of reading $0.- Helpers: image upload, the brand icon (warns that a brand change can pause delivery until review), pixel plus a
lead_createdconversion event, and avalidate_onlytest event. - Growth suite:
adsops_openai_ad_check(chat_card limits, allowed domains, required UTM, duplicates),adsops_openai_draft_campaign(a business brief becomes a paused, capped desired state the engine accepts),adsops_openai_ab_report(winner with intervals and Bonferroni-corrected significance),adsops_openai_send_conversions(CRM leads to the Conversions API, hashed locally, validate-only unlesscommit=true) andadsops_openai_audience(hashed customer lists to custom audiences, plan-only unlesscommit=true).
Live status (2026-11-06): the read paths, live spend reads, the guard and validation ran against a real OpenAI Ads account
in read-only mode (live proof); that run found and fixed a full-hour insights rule the mock had
missed. Creates, image upload, activation and conversion/audience commits are tested offline (tests/qa_openai_ads.py,
tests/qa_openai_suite.py) but have not yet run live. Section 9 of the guide lists what is still unverified.
Local business and Google LSA
For a small service business (plumber, HVAC, roofer, cleaner, lawyer...) that wants leads from Google and AI assistants and runs everything from its own Claude. Setup is one prompt in Claude Desktop's Code tab:
Set me up with AdRails for my business. Read https://adrails.dev/start/ and follow the steps for Claude.
Claude runs adsops-mcp setup --business "Name" --install-desktop (workspace in ~/AdRails, registered in Claude Desktop with a backup
of its config), interviews the owner, builds the plan, and later adsops-mcp connect --config ~/AdRails/config.json --client-file <Desktop-app OAuth client JSON> (one Google sign-in: loopback on 127.0.0.1 + PKCE, refresh token saved chmod 600 beside the config, the
reachable Google Ads accounts added). No ad account is needed to start: the lead book, plan, AEO and reviews work without one.
MCP prompts start, morning and weekly chain these into the daily routine. Live status: every LSA query (leads, conversations, campaigns, verification) runs against the live Google Ads API v24
(verified 2026-11-06; tests/qa_local_suite.py pins each selected field to Google's live field list). Lead feedback submits through
ProvideLeadFeedback the first time an account has a lead to rate, and returns Google's credit decision. LSA budgets, bids, hours and pausing are not in the API (dashboard only). Lead-book, profile and plan files
live beside the config (chmod 600); the audit chain records only structural arguments for these tools, never customer names or notes.
Playbooks. adsops_playbook serves the strategy behind the tools, written for the agent to execute: aeo (a 7-day fast path
for a new business, the AI-visibility panel method, the pages engines quote, schema, entity consistency, the ads-to-AEO bridge,
cadence, myths), local-leads (speed-to-lead, LSA, reviews, follow-up cadence), google-business-profile (to 100% and kept
there), google-ads (tracking honesty, negatives, landing pages, caps, when to stop) and chatgpt-ads (drafts, creative,
attribution, the guard). Every task in the 90-day plan links the playbook section behind it. Every step names its
tool and a done-check; every claim is marked tested or early evidence. Files: adsops/playbooks/*.md.
Results and Business Profile. adsops_results compares the month before AdRails with now (leads, jobs won, cost per lead by
channel, first-response time, AI share of answer, reviews, plan progress); missing data is listed as a gap, never a zero.
action=case_study returns a shareable summary with trade and city only (name only with permission), no customer data, and a
quote that is refused if it holds a phone or email. adsops_gbp scores a Google Business Profile snapshot to 100 with ranked
fixes, checks the description against Google's rules and plans two posts a week. The monthly prompt runs all of it.
Staying current. The server tells the agent when a newer AdRails is out: the MCP initialize instructions carry a one-line
notice (from a cache, so startup never waits) and adsops_update_check returns the release notes and the upgrade command.
At most one request a day to https://pypi.org/pypi/adrails/json, no account or usage data sent; off with
ADRAILS_NO_UPDATE_CHECK=1 or "update_check": false. With the owner's OK the agent runs adsops-mcp upgrade, which pre-fetches
the release and raises the version floor in Claude Desktop's config (backup first, other servers untouched).
Works with Google's official google-ads-mcp
Run both servers side by side in the same MCP client. Google's server answers ad-hoc GAQL
exploration and never writes; AdRails is the only write path. The agent reads with the official
server, proposes a change to a desired-state or policy file, and applies it through
adsops_plan / adsops_apply — with caps, a validate pass, an account lock, and an audit line
in between. Both speak JSON-RPC over stdio, so one .mcp.json entry each is the whole setup.
License
Apache-2.0.
Source: README.md at commit 9e0018e
Tools
0Version history
4- v0.8.0LatestNov 7, 2026
- v0.4.2Nov 5, 2026
- v0.2.2Oct 29, 2026
- v0.2.0Oct 22, 2026
Score
50% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 days25 pts
Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.
- Auth declared10 pts
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts
