antonioac1

AdRails (adrails.dev)

Community antonioac1
Updated

AI agents run Google Ads and ChatGPT Ads with guardrails in code: plan/validate/commit, approvals, audit log. Plus AEO and direct-mail tools. Local, open source.

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 searchterms 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 Adscredentials, read from the sources you configure (a service-account JSON file, a static token, ora secret-manager lookup), and they never leave your machine. It talks to the ad platforms youconnect, to PyPI once a day for an update check (off with ADRAILS_NO_UPDATE_CHECK=1), and toadrails.dev only when your agent calls adsops_paid.
  • Your caps. Caps live in a policy file that no AdRails tool writes; with require_plan_idon, every commit must quote a fresh, single-use plan_id issued for that exact plan; and achange that adds spend exposure is refused while the scheduled guard's heartbeat is stale.
  • Free, with optional paid extras. Everything runs free. Three hosted extras are paid per job insats (on-chain) or USDC (x402), with no account: an AI visibility panel, a second opinion before achange goes live, and a Bitcoin timestamp of your audit log. Your agent shows you the price first;the tool never pays.

New in 0.10.0: the measurement loop. adsops_metrics_pull keeps a daily local history of Google Ads, Google Search Console, AI-visibility panels and leads; adsops_search_console finds the cheapest organic wins and the search terms you pay for while already ranking top-3; adsops_impact answers "did that change work?" against a counterfactual with a 90% range and a placebo test. All local, all read-only towards Google. 87 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. The server itself is never hosted; only the optional paid extras run on adrails.dev.

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

python3 -m adsops --selftest           # 430 unit checks, everything mocked
python3 tests/qa_invariants.py         # 200 random sequences against a simulated Google Ads account
python3 tests/qa_inputs.py             # hostile policy/state/config inputs (NaN, Infinity, bad dates, bad UTF-8, ...)
python3 tests/qa_secrets.py            # canary tokens injected everywhere; none may reach output or logs

The QA suites were written adversarially by a separate agent with no write access to adsops/, and every finding was fixedor 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 guardcompares spend with numbers you wrote and issues pause operations. That is containment, notbilling control.
  • Google can deliver up to 2x the average daily budget on a given day. A daily budget is anaverage, 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 MCPprocess is not a monitor: while the machine sleeps no checks run, spend keeps moving, and theheartbeat goes stale.
  • An agent with shell access as the same user can bypass any local tool. AdRails constrainsthe 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 abad one is also awkward to withdraw.
  • You are responsible for your account and your spend. Keep platform-side billing limits asthe real ceiling.

Quickstart

1. Install.

# from PyPI (https://pypi.org/project/adrails/)
pipx install adrails     # console script: adsops-mcp
# or run without installing:
uvx --from adrails adsops-mcp --config /absolute/path/config.json

No other dependencies. Service-account tokens are signed with your system opensslwhen google-auth isn't installed (install google-auth if you prefer it).

The core is stdlib-only. Check it offline (everything mocked, no network):

python3 -m adsops --selftest      # or: deploy/smoke_docker.sh

2. Write a config. Every account, credential source and path lives here; paths are~-expanded at load time.

{
  "tenant": "your-tenant",
  "meter_log": "~/.adsops/meter.jsonl",
  "audit_log": "~/.adsops/audit.jsonl",
  "plan_ledger": "~/.adsops/plans.jsonl",
  "lock_dir": "~/.adsops/locks",
  "guard_heartbeat": "~/.adsops/guard.heartbeat",
  "credentials": {"google_ads": {"type": "service_account",
      "sources": [{"kind": "file", "path": "~/.adsops/sa.json"}]}},
  "google_ads": {"api_version": "v24", "end_field": "end_date_time", "login_customer_id": null},
  "commit_policy": {"require_clean_git": true, "allow_commit": true, "allow_set": true},
  "accounts": [{"key": "main", "platform": "google_ads", "customer_id": "000-000-0000",
      "campaigns": [{"key": "search_core", "state_file": "~/ads/search_core.json",
                     "policy_file": "~/ads/search_core.policy.json"}]}]
}

commit_policy defaults to require_clean_git: true and allow_commit: false, so commits andadsops_set stay off until you turn them on.

3. Write the policy file for the campaign (next section), and commit both files to git sogit diff is the change review.

4. Register the server in your MCP client. Diagnostics go to stderr; the server speaksJSON-RPC 2.0 over stdio, one object per line.

{
  "mcpServers": {
    "adrails": {
      "command": "python3",
      "args": ["-m", "adsops", "--config", "/absolute/path/config.json"]
    }
  }
}

5. Schedule the guard. A chat session cannot watch spend, so the pause-only spend check runsfrom your OS scheduler every 15 minutes. Templates for launchd, systemd and cron live indeploy/; render the placeholders (__PYTHON__, __CONFIG__, __PKG__, __LOGDIR__) and readdeploy/GUARD.md before installing:

PY=$(command -v python3); CFG=/absolute/path/config.json
PKG=/absolute/dir/containing/adsops; LOGDIR="$HOME/.adsops/logs"
mkdir -p "$LOGDIR"
sed -e "s|__PYTHON__|$PY|g" -e "s|__CONFIG__|$CFG|g" \
    -e "s|__PKG__|$PKG|g" -e "s|__LOGDIR__|$LOGDIR|g" \
    deploy/launchd/com.adsops.guard.plist.template \
    > ~/Library/LaunchAgents/com.adsops.guard.plist
plutil -lint ~/Library/LaunchAgents/com.adsops.guard.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.adsops.guard.plist

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.

pip install adrails

# Claude Code
claude mcp add adrails -- python3 -m adsops --config /absolute/path/config.json
# Grok CLI (it also reads ~/.claude.json, .cursor/mcp.json and .mcp.json)
grok mcp add adrails -- python3 -m adsops --config /absolute/path/config.json

Cursor or a project .mcp.json:

{"mcpServers":{"adrails":{"command":"python3","args":["-m","adsops","--config","/absolute/path/config.json"]}}}

Claude Desktop: install the .mcpb bundle from the latest release.More: https://adrails.dev/docs/mcp-clients/

Tools

Tool What it does
adsops_campaigns Lists the campaigns this deployment may operate: keys, accounts, state files, and the active commit policy.
adsops_status Live read for one campaign: status, end date, daily budget, spend today and lifetime, negative count, whether caps come from the policy file, and the guard heartbeat's age. When the state file manages URL fields it also returns urls: the campaign final URL suffix and, per ad group, its custom parameters, final URL suffix and the final URLs of each enabled ad.
adsops_terms Clicked search terms for the last N days (1–90, default 14): term, matched keyword, clicks, cost.
adsops_plan Pure diff of the desired-state file against live state; writes nothing. Covers budget, status, end date, negatives and the URL fields below. Returns the operations, notes (one url: ... line per URL change), ops_count, the exposure increases it found, and a single-use plan_id with its expiry.
adsops_apply Validate-only unless commit=true. A commit needs commit_policy.allow_commit, a clean git-tracked state file, a fresh guard heartbeat when the change adds exposure, and the plan_id from adsops_plan when the policy requires it.
adsops_set Proposes a budget / status / end-date / negatives change with a short reason; checks it against policy caps and live state, writes the state file and commits it to git. dry_run=true returns a plan_id instead of writing; apply=true needs that plan_id when the policy requires it.
adsops_guard Reads spend and pauses the campaign if the daily or lifetime threshold is crossed. Pause only: it never enables, raises a budget or edits keywords. Validate-only unless commit=true.
adsops_usage Metering summary for the tenant: calls, errors, units, ops and median latency per tool.
adsops_negatives_suggest Read-only. Classifies the last N days (1–90, default 30) of search terms (renter, job, HOA/condo, vacation, DIY/software, out-of-area, competitor brand) with deterministic rules and an optional local-model hook you supply, and proposes PHRASE negatives with cost and click evidence. Skips terms and phrases your existing negatives already cover, never suggests words on your protect list, and returns a ready-to-paste negatives_add patch plus a basis note. It writes nothing: the patch goes through the desired-state file like any other change.
adsops_lp_diagnose Read-only. Per ad group, Google's quality-score components (expected CTR, ad relevance, landing page experience) plus a plain HTTP check of each final URL: status, response time, bytes, and whether the title and H1 echo the ad group's keywords and town. Returns plain-English findings, worst first. No browser and no JavaScript, so a headline built by script shows as missing.
adsops_competitors_report Read-only. The AdRails Intel weekly report from the free sources (Auction Insights you imported, cached Transparency Center data, your own stats snapshots). Needs intel_config in the config; makes no network call.
adsops_auction_insights_import Imports an Auction Insights CSV/TSV export or a pasted table into the local intel database (replaces rows for the same date range). Writes only that local SQLite file. Needs intel_config.

New in 0.4.0: enterprise controls, automation and an AEO toolkit (28 tools)

Tool What it does
adsops_conv_audit Read-only. Audit conversion tracking for the campaign's account: primary goals, dead tags, double counting, auto-tagging, lead counting, smart bidding without enough conversions. Returns a 0-100 score, grade, and ranked findings with fixes. Run this before trusting any CPA or optimising toward conversions.
adsops_diagnostics Read-only PPC diagnostics bundle for one campaign: wasted spend (zero-conversion search terms), impression share lost to budget vs rank, cost-weighted quality score with weak components, and day-over-baseline anomalies (z-score). Returns a ranked headline list plus details.
adsops_rsa_validate Pure check, no account access. Validate a Responsive Search Ad draft against Google's limits and common disapproval causes (lengths incl. double-width chars, pins, duplicates, caps, punctuation, phone numbers, unsubstantiated superlatives, banned phrases) before any write. Returns errors, warnings and fixes.
adsops_rules_check Read-only. Evaluate the automation rules in the campaign's POLICY file (founder-owned, agents can't edit it) against live metrics. Returns PROPOSALS and a desired-state patch; nothing is applied. Rules can only pause, add negatives, alert, or move budget inside the policy caps.
adsops_monitor_check Read-only. Run monitors (CPA spike, pacing over/under, month forecast, zero impressions, disapprovals, budget-limited) over live snapshots of every configured campaign (or one). New alerts are de-duplicated for 24 h; with deliver=true they are posted to the webhook named in config alerts.webhook_env.
adsops_brief Read-only. A short markdown brief across every configured campaign: spend today, conversions, blended CPA and the top issues from the monitors. Built for a daily Slack/email post.
adsops_fleet Read-only. Fleet view across every configured account and campaign: spend, budget, conversions and CPA by account and platform, plus the AGGREGATE cap check (config fleet_caps), which assumes platforms may deliver up to 2x the daily budget.
adsops_realloc_suggest Read-only. Recommend moving daily budget from weak campaigns to budget-limited strong ones inside each policy's caps. The total never rises. Returns moves plus per-campaign desired-state patches to review.
adsops_drift Read-only. Who changed the account outside AdRails? Reads Google's change history for the last N days (max 30) and flags out-of-band edits (UI, scripts, auto-applied recommendations), highlighting budget, status and bidding changes.
adsops_keyword_ideas Read-only. Keyword Planner ideas (searches, competition, top-of-page bid range) for seed keywords and/or a URL, clustered by theme with an intent label. Needs a Basic-access developer token.
adsops_offline_conv_plan Build (never send) an offline / enhanced conversion upload from CRM rows. Emails, phones and names are normalised and SHA-256 hashed LOCALLY, so raw PII never leaves this machine. Returns a redacted preview and counts; validate_only stays true.
adsops_history Read-only. Git history of the campaign's desired-state file: every change agents or lanes made, newest first.
adsops_rollback Undo: build the desired state as of an earlier commit (guards, account and campaign ids are never reverted, and negatives are never removed). With commit=true it writes and git-commits the state file; then run adsops_plan and adsops_apply as usual. Nothing reaches the ad account from this tool.
adsops_approval_request Ask a human to approve a plan before it can be committed (when config approvals.mode is on). Returns approve/reject tokens to send to the approver; the tokens are HMAC-signed and expire.
adsops_approval_decide Record a human decision using a signed approve/reject token. Forged, expired or reused tokens are refused.
adsops_approvals List approval requests (newest first), optionally by status.
adsops_audit_verify Verify the tamper-evident audit chain of every mutating call (hash-linked entries). Returns ok, entry count, the first bad entry if any, and a head digest to publish so later truncation is detectable.
adsops_audit_tail The last N audit-chain entries (redacted), newest first.
adsops_sbom Supply-chain manifest of this AdRails install: a CycloneDX 1.5 SBOM and a SHA-256 manifest of every package file, so you can verify the code you run is the code that was released.
adsops_whoami This server's identity and controls: version, principal and roles (RBAC), approval mode, audit chain path, and whether a guard heartbeat is configured.
aeo_site_audit Read-only. Can AI answer engines reach this site? Checks robots.txt rules for 18 AI and search crawlers (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, PerplexityBot, Google-Extended, Bingbot, Applebot...), llms.txt, the sitemap, and the homepage's noindex/canonical/body-text signals. Returns a 0-100 score and ranked fixes.
aeo_page_audit Read-only. Answer-readiness of one page: answer-first paragraph, question headings, FAQ, structured data, specifics, freshness, citations, scannability, plus a JSON-LD audit (required/recommended fields per type) and crawler directives. Returns scores, extractable answer blocks, and missing questions.
aeo_llms_txt Read-only. Validate a site's /llms.txt (llmstxt.org format), or build one: pass pages [{url,title,description,section}] with title and summary and it returns a ready file that passes validation.
aeo_schema_generate Pure. Generate valid JSON-LD for a page: kind = faq (qas [[q,a]...]), software (name,url,description,price), org (name,url,logo,same_as) or breadcrumbs (trail [[name,url]...]). Returns the object, its own audit, and a ready

MCP Server · Populars

MCP Server · New

    roman-rr

    Trading Signals

    17 triggers × 44 algorithms × 3 AI experts — live crypto trading signals with reasoning for Claude Code, Codex, Cursor & 30+ agents. Free key; Pro adds entry, SL and TP.

    Community roman-rr
    OyadotAI

    Oya Browser

    The browser control plane for AI agents. One API over Oya Cloud, Browserbase, Steel, Anchor, Browser Use and your own Chrome, with persistent personas, CAPTCHA and MFA handling, and live human takeover.

    Community OyadotAI
    unibaseio

    Membase

    Memory layer for AI agents — SDKs, CLI and MCP server for Membase

    Community unibaseio
    EternalNight996

    🧠 memory-eternal — a second brain for your AI

    Your AI forgets every session. Give it a memory you can audit: local-first, review-gated agent memory — one SQLite vault for DSH, Claude Code, Codex, Cursor, OpenClaw, Hermes and MengBot (any MCP client), with an immutable audit log and a token ledger. Agents propose, humans approve. MIT, self-hosted, no cloud.

    Community EternalNight996
    quarkiverse

    Model Context Protocol Servers in Quarkus/Java

    Model Context Protocol Servers in Quarkus

    Community quarkiverse