Skip to main content
Glama

quantum-ads-mcp

CI CodeQL OpenSSF Scorecard Google Ads API Python MCP connectors mypy coverage License

The most complete open Model Context Protocol control plane for the Google marketing stack — 21 connectors, read + guarded write, Ed25519 signed audit, OAuth 2.1 multi-tenant.

Architecture · The 21 connectors · Install · Configure · Safety · Autonomy · Observability · Roadmap

A sovereign Google marketing-agency control plane over the Model Context Protocol: a shared core (multi-tenant auth, version resilience, a GAQL/query engine, a safety spine with a signed audit ledger) and 21 pluggable per-product connectors spanning the entire Google marketing stack. Built for an AI agent to operate that stack at full granularity, safely.

Status. The full connector surface is built and tested at the logic layer: read + guarded write across 21 products, every tool exercised by mocked tests (no live SDK needed). The per-product live SDK glue is isolated in sdk.py modules and smoke-gated (verified against real APIs with your own credentials, not in CI — Google Ads API v26 included; see docs/LIVE_VALIDATION_RESULTS.md). Served over stdio or Streamable HTTP behind OAuth 2.1 (JWT bearer tokens, RFC 9728 metadata, per-tenant credentials and scopes), verified end to end against a real HTTP server. Read-only by default. Every "most X" claim here is meant to be verifiable.

Contents

Why · The 21 connectors · Architecture · Core · Install · Configure · Run · Safety & honest claims · Roadmap

Related MCP server: Google Ads MCP Server

Architecture

flowchart TD
    Client["MCP client (AI agent)"] -->|"stdio · or OAuth 2.1 HTTP (multi-tenant)"| Server["FastMCP server"]
    Server --> Core["Sovereign core: auth · version resilience · GAQL/query engine · safety spine (read-only default + Ed25519 signed audit) · registry / meta-tool"]
    Core --> C1["google_ads · ga4 · gtm · merchant"]
    Core --> C2["datamanager · searchconsole · youtube"]
    Core --> C3["dv360 · cm360 · sa360"]
    Core --> C4["bigquery · adh · looker · meridian"]
    Core --> C5["vertex · trends · gbp · language · workspace · recaptcha"]
    C1 -. "smoke-gated sdk.py" .-> G["Google APIs (Ads v26, GA4, GTM, Merchant, …)"]
    C4 -. "smoke-gated sdk.py" .-> G

Why this exists

Surveying June 2026: Google's official MCP is read-only (3 tools); the best open write-capable servers are pinned to sunsetting API versions, untested, single-product, or have no safety layer; the capable commercial ones are closed and remote-only. No open, self-hostable server is simultaneously complete across the stack, write-capable, safe, tested, version-resilient, and multi-product. Quantum ADS fills that gap.

The 21 connectors

Domain

Connectors

Paid media

Google Ads (v26), DV360, CM360, SA360

Measurement

GA4 (Data + Admin), Data Manager API (Customer Match + conversions), Search Console

Catalog / tagging

Merchant API, Google Tag Manager (+ server-side, Consent Mode v2)

Channels

YouTube (Data + Analytics + Reporting), Business Profile, Trends

Warehouse / BI / MMM

BigQuery (cost-guarded), Ads Data Hub, Looker, Meridian (Bayesian MMM)

Creative / ops

Vertex AI (Gemini 3.8 / Gemini image / Veo 3.1, pure REST), free text-to-video (Wan 2.2 / LTX-Video on Hugging Face ZeroGPU, no billing), creative studio + campaign insights (anomalies, pacing, search-term mining, GA4 reconciliation; free OpenAI-compatible / SDXL / FLUX fallback when Vertex is unbilled), Cloud Translation + NL, Workspace (Sheets/Drive/Slides), reCAPTCHA Enterprise

Full tool catalog, backend keys, and required SDK packages: CONNECTORS.md.

Beyond the curated tools, google.api.call runs any write method of 25 marketing Google APIs (653 write methods, 1,180 in all), checked offline against each API's Discovery contract, scope-checked, guarded, audited and reversible where the API allows — the way ads.mutate covers every Google Ads resource.

Core (shared by every connector)

  • Multi-tenant auth + secret store — credentials load from env / secret manager and never reach the LLM.

  • Version resilience — pinned Google Ads API v26 from one DEFAULT_API_VERSION, official sunset table + guard (days-until-sunset), offline GAQL field conformance (every queried field resolved against the SDK protos of the pinned version), ads.api.version_delta (exact resources/services/enums added or removed between two versions), and unknown-enum tolerance for Google's monthly releases.

  • Query engine — GAQL build / validate (single-FROM, no-OR, segment rules, 37-month cap) / stream / flatten, with quota token-buckets + exponential backoff. SA360 uses the same query shape.

  • Safety spine — read-only by default; every mutation is guarded: validate_only preview → two-step confirm token → Ed25519-signed, hash-chained audit ledger, persisted across restarts and verifiable with audit.status. Consent-aware (Data Manager / Consent Mode v2) and cost-aware (BigQuery dry-run, $6.25/TiB).

  • Connector registry + meta-tool discovery (list_capabilities, describe_tool) and env-based connector selection (QUANTUM_ADS_CONNECTORS) so the tool list stays focused.

Install

git clone https://github.com/Casius999/quantum-ads-mcp.git
cd quantum-ads-mcp
uv sync --extra dev
uv run pytest -m "not live"      # 600+ tests incl. Hypothesis property fuzzing, no credentials

Other ways in (each release ships them, with signed provenance):

  • One-click bundle — download quantum-ads-mcp.mcpb from the latest release and open it in Claude Desktop; it asks for the developer token and OAuth values and keeps the secrets in the OS keychain.

  • Container — ghcr.io/casius999/quantum-ads-mcp serves Streamable HTTP on :8080 (gh attestation verify oci://ghcr.io/casius999/quantum-ads-mcp:<version> -R Casius999/quantum-ads-mcp). Deployment on Cloud Run, and "Sign in with Google" for a team: docs/DEPLOY.md.

  • PyPI / MCP Registry — uvx quantum-ads-mcp and the registry entry io.github.casius999/quantum-ads-mcp (server.json) publish from the release workflow once PyPI trusted publishing is enabled for the repository.

Per-connector live SDKs (e.g. google-analytics-data, google-api-python-client, google-cloud-bigquery) are installed only when you wire a connector's live backend — see CONNECTORS.md. The core + mocked test suite need none of them.

Configure

Credentials come from process environment variables (set them in your MCP client's env block; no secrets on disk). Every connector whose credentials resolve is wired at startup — lazily, so a bad credential only affects its own connector; the rest answer BACKEND_NOT_CONFIGURED.

GOOGLE_ADS_USE_PROTO_PLUS=True
GOOGLE_ADS_API_VERSION=v26
GOOGLE_ADS_DEVELOPER_TOKEN=...
GOOGLE_ADS_CLIENT_ID=....apps.googleusercontent.com
GOOGLE_ADS_CLIENT_SECRET=...
GOOGLE_ADS_REFRESH_TOKEN=...               # also serves GA4/GTM/Merchant/YouTube/... if scoped
GOOGLE_OAUTH_CLIENT_ID=...                 # optional dedicated non-Ads credential (+ _SECRET, _REFRESH_TOKEN)
GOOGLE_CLOUD_PROJECT=my-project            # BigQuery, Vertex, Language, reCAPTCHA + quota routing
GOOGLE_VERTEX_LOCATION=global              # optional
LOOKERSDK_BASE_URL=...                     # Looker (+ LOOKERSDK_CLIENT_ID / _CLIENT_SECRET)
QUANTUM_ADS_READ_ONLY=true                 # writes refused until set to false
QUANTUM_ADS_CONNECTORS=google_ads,ga4,gtm  # optional: mount a subset (default: all)
QUANTUM_ADS_AUDIT_DIR=~/.quantum-ads/audit # signed audit ledger (default shown; empty = in memory)

Full credential matrix: auth & guardrails · template: .env.example.

Never commit .env, google-ads.yaml, or client_secret*.json — all gitignored, and gitleaks runs in CI. See SECURITY.md.

Run

quantum-ads-mcp                          # stdio MCP server (what MCP clients launch)
quantum-ads-mcp serve --transport http   # remote: Streamable HTTP behind OAuth 2.1

Wire the stdio form into your MCP client (Claude Desktop / VS Code). The HTTP form is an OAuth 2.1 resource server: every request needs Authorization: Bearer <JWT> (signature, iss, aud bound to the resource URL, expiry and the ads:read scope are checked; ads:write is required for guarded writes), and it refuses to start without token verification configured.

# With your identity provider (Entra ID, Okta, Auth0, Keycloak, Google, ...):
QUANTUM_ADS_OAUTH_JWKS_URI=https://idp.example/.well-known/jwks.json
QUANTUM_ADS_OAUTH_ISSUER=https://idp.example
QUANTUM_ADS_PUBLIC_URL=https://ads.agency.example   # audience = <public url>/mcp

# ...or self-hosted, with the built-in Ed25519 issuer:
quantum-ads-mcp keygen                               # prints QUANTUM_ADS_OAUTH_PUBLIC_KEY_FILE=...
quantum-ads-mcp token --tenant acme --scope "ads:read ads:write" --ttl 3600

# Multi-tenant: one credential set per tenant, selected by the token's `tenant` claim.
QUANTUM_ADS_TENANTS_SECRET=quantum-ads-tenants   # Google Secret Manager (recommended), or
QUANTUM_ADS_TENANTS_FILE=tenants.json            # {"tenants": {"acme": {"GOOGLE_ADS_...": "..."}}}

With an external identity provider the server publishes Protected Resource Metadata (RFC 9728) at /.well-known/oauth-protected-resource/mcp, so MCP clients discover where to sign in. Each tool call runs as the token's tenant: its own backends, Google Ads client, write permission and audit identity — never another tenant's or the operator's. Per-tenant rate limiting applies.

Safety & honest claims

Read-only by default; tokens never reach the model; zero secrets in the repo. CI runs lint, strict type-check, the full test suite (≥90% coverage gate), CodeQL, OpenSSF Scorecard, and gitleaks.

The defensible "most powerful" claim is coverage breadth + engineering rigor + safety + version resilience, each verifiable — not "record ad performance" (that depends on budget, creative, and Google's auction, not the server). Documentation states only what the test suite proves; the per-product live SDK calls are smoke-gated and labelled as such.

Google Ads: create and change anything

ads.mutate sends official MutateOperation JSON for any of the API's 65 mutable resource kinds in one GoogleAdsService.Mutate request (temporary negative ids link new objects; atomic unless partial_failure), validated offline against the pinned version's protos first. ads.campaign.create_search, ads.campaign.create_pmax and ads.campaign.create_demand_gen build complete Search, Performance Max and Demand Gen campaigns in one call; A/B experiments (ads.experiment.create / schedule / promote / end), asynchronous BatchJobs for large changes (ads.batch.submit / status), Reach Planner forecasts (allowlisted tokens), Keyword Planner ideas, recommendations (list / apply / dismiss) and ads.mutate.schema complete the plane. Every applied ads.mutate records its inverse in the signed audit trail — updated fields are read back first, created objects are removed on undo — and ads.rollback replays it.

Agent autonomy and MCP-native features

The agent is the decision-maker: it holds the data. With QUANTUM_ADS_WRITE_MODE=auto a write is validated by the API (validate_only) and applied in the same call — a failed validation never applies; confirm (default) keeps a two-call preview/commit. Guardrails are opt-in: a QUANTUM_ADS_POLICY_FILE (default + per-tenant sections) can forbid or allow operations by glob, cap any *amount_micros, bound operations per call, restrict UTC hours and limit writes per tenant per day — no file, no limits.

  • Tool annotations on every tool (read_only_hint, destructive_hint, idempotent_hint, open_world_hint, title) so clients auto-run reads and treat destructive writes accordingly.

  • Resources: quantum-ads://capabilities, quantum-ads://google-ads/versions, quantum-ads://google-ads/field-deltas, quantum-ads://session (tenant, scopes, write mode, effective policy).

  • Prompts (playbooks chaining the tools end to end): weekly account audit, Search campaign launch, PMax diagnosis, search-terms mining, budget reallocation, Merchant feed optimisation, GA4↔Ads reconciliation, demand research, creative studio.

  • Background tasks (QUANTUM_ADS_TASKS=true, uv sync --extra tasks): video, Veo, Gemini, BigQuery runs, Meridian fits and ADH queries run as MCP tasks (SEP-2663) on modern-protocol (2026-07-28) clients — a task handle to poll instead of a held call; every long-running tool also carries a timeout.

  • Tool search (QUANTUM_ADS_TOOL_SEARCH=true): the catalog collapses to search_tools + call_tool (BM25) plus the meta tools, so an agent loads only the tools a task needs.

Observability

Every tool call writes one JSON line to stderr — tool, tenant, outcome, error code, duration, trace id; never arguments or results — and health reports call counters. FastMCP's OpenTelemetry spans carry quantum_ads.tenant / outcome / error_code and are exported over OTLP when the standard variables are set (uv sync --extra observability):

OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318   # or OTEL_TRACES_EXPORTER=console (stderr)
OTEL_SERVICE_NAME=quantum-ads-mcp                   # default
QUANTUM_ADS_LOG_CALLS=false                         # silence the per-call JSON lines

Audit anchoring

The audit ledger is signed and hash-chained; audit.anchor additionally publishes its head to Bitcoin through public OpenTimestamps calendars (only a SHA-256 leaves the machine) and saves a standard .ots proof under <audit dir>/anchors/, verifiable by anyone with ots verify. audit.anchor_status upgrades pending proofs once Bitcoin confirms them and checks each one against the block's merkle root from a public explorer rather than trusting the calendar (bitcoin_verified; uv sync --extra anchoring).

Roadmap

Waves 1–6 of docs/ROADMAP_V1.md are in: MCP-native foundation, Google Ads complete, insights and creative studio, the generic Google API plane, Sign in with Google and distribution, agent evals and mutation testing. Still open:

  • typed output schemas per tool, record/replay cassettes for credential-free CI, async I/O;

  • live checks that need accounts the project cannot create (ADH, CM360, Business Profile, DV360), a real Google sign-in on a public URL, and PyPI trusted publishing (one setting on pypi.org);

  • paid Veo generation stays opt-in (VERTEX_LIVE_VEO=1).

License

MIT — see LICENSE.

— Julien Compain · NovaQuantiX

Available Tools

163 tools
adh.customers.listAdh customers listA
Read-onlyIdempotent

List Ads Data Hub customers (accounts) the authenticated user can access.

Inputs: no named parameters. Returns: object

adh.jobs.getAdh jobs getA
Read-onlyIdempotent

Poll a started ADH analysis-query run by operation name (status + result reference).

Inputs: operation_name (required). Returns: object

adh.queries.listAdh queries listB
Read-onlyIdempotent

List the stored analysis queries owned by an ADH customer.

Inputs: customer_id (required). Returns: object

adh.query.createAdh query createB

Create a stored ADH analysis query (guarded: validate_only preview + confirm token). Creates only; does not run the query.

Inputs: title (required), query_text (required), customer_id (required), confirm. Returns: object

adh.query.startAdh query startA
Read-onlyIdempotent

Start a stored ADH analysis query over a date range; returns an operation/job ref (async, privacy-checked run — poll adh.jobs.get for the result).

Inputs: end_date (required), query_id (required), start_date (required), customer_id (required). Returns: object

ads.api.version_deltaAds api version deltaA
Read-onlyIdempotent

Exact resources/services/enums added or removed between two API versions (computed from the installed SDK; plan an upgrade without a token).

Inputs: to_version, from_version. Returns: object

ads.batch.statusAds batch statusB
Read-onlyIdempotent

Status of a BatchJob and, once done, its per-operation results.

Inputs: batch_job (required), customer_id (required), limit. Returns: object

ads.batch.submitAds batch submitA

Submit a large mutation (thousands of MutateOperations) as an asynchronous BatchJob; track it with ads.batch.status.

Inputs: operations (required), customer_id (required), confirm. Returns: object

ads.budget.updateAds budget updateC
DestructiveIdempotent

Update a campaign budget amount in micros (guarded).

Inputs: budget_id (required), customer_id (required), amount_micros (required), confirm. Returns: object

ads.campaign.create_demand_genAds campaign create demand genB

Create a complete Demand Gen campaign in one atomic call: budget, campaign, ad group targeting, image/logo assets and a multi-asset ad.

Inputs: name (required), final_url (required), headlines (required), customer_id (required), +11 more. Returns: object

ads.campaign.create_pmaxAds campaign create pmaxA

Create a complete Performance Max campaign in one atomic call: budget, bidding, targeting, text + image assets (base64, e.g. from vertex.imagen.generate), asset group, and brand assets (business name, logo) under brand guidelines.

Inputs: name (required), images (required), final_url (required), headlines (required), +13 more. Returns: object

ads.campaign.set_statusAds campaign set statusB
DestructiveIdempotent

Pause/enable a campaign (guarded: validate_only preview + confirm token).

Inputs: status (required), campaign_id (required), customer_id (required), confirm. Returns: object

ads.change_historyAds change historyD
Read-onlyIdempotent

Change history (audit=change_event, delta=change_status).

Inputs: customer_id (required), mode, limit, date_range. Returns: object

ads.experiment.createAds experiment createB

Create an A/B experiment on a campaign: control + treatment arms with a traffic split. Edit the treatment draft (arm in_design_campaigns) with ads.mutate, then schedule.

Inputs: name (required), customer_id (required), base_campaign_id (required), suffix, +3 more. Returns: object

ads.experiment.endAds experiment endC
Destructive

End a running experiment.

Inputs: experiment (required), customer_id (required), confirm. Returns: object

ads.experiment.promoteAds experiment promoteC
Destructive

Promote an experiment's treatment changes to the base campaign.

Inputs: experiment (required), customer_id (required), confirm. Returns: object

ads.experiment.scheduleAds experiment scheduleC
Destructive

Start (schedule) an experiment.

Inputs: experiment (required), customer_id (required), confirm. Returns: object

ads.fields.deltasAds fields deltasB
Read-onlyIdempotent

Curated 2026 field/view/resource deltas (v22 to v26).

Inputs: no named parameters. Returns: object

ads.fields.new_viewsAds fields new viewsC
Read-onlyIdempotent

New 2026 reporting views.

Inputs: no named parameters. Returns: object

ads.gaql.queryAds gaql queryC
Read-onlyIdempotent

Run an arbitrary read-only GAQL query.

Inputs: query (required), customer_id (required). Returns: object

ads.keyword_ideasAds keyword ideasB
Read-onlyIdempotent

Keyword Planner ideas from seed keywords and/or a URL: monthly searches, competition, top-of-page bid range.

Inputs: customer_id (required), url, limit, keywords, +2 more. Returns: object

ads.mutateAds mutateB
Destructive

Create/update/remove ANY Google Ads resource: MutateOperation JSON list (e.g. {'campaign_operation': {'create': {...}}}), temporary ids (-1) link new objects, atomic unless partial_failure. See ads.mutate.schema.

Inputs: operations (required), customer_id (required), confirm, partial_failure. Returns: object

ads.mutate.schemaAds mutate schemaA
Read-onlyIdempotent

List the mutate operation kinds, or the fields of one kind, for the pinned version.

Inputs: operation_kind. Returns: object

ads.reach.forecastAds reach forecastB
Read-onlyIdempotent

Reach Planner forecast (reach curve, on-target impressions) for a media plan: products [{code, budget_micros}], location, currency, duration.

Inputs: products (required), customer_id (required), location_id (required), currency_code (required), +1 more. Returns: object

ads.reach.locationsAds reach locationsB
Read-onlyIdempotent

Reach Planner: plannable locations (ids for ads.reach.products / forecast).

Inputs: no named parameters. Returns: object

ads.reach.productsAds reach productsC
Read-onlyIdempotent

Reach Planner: plannable video/display products for a location.

Inputs: location_id (required). Returns: object

ads.recommendations.applyAds recommendations applyC
Destructive

Apply Google Ads recommendations by resource name (from ads.recommendations.list).

Inputs: customer_id (required), resource_names (required), confirm. Returns: object

ads.recommendations.dismissAds recommendations dismissC
Destructive

Dismiss Google Ads recommendations by resource name.

Inputs: customer_id (required), resource_names (required), confirm. Returns: object

ads.recommendations.listAds recommendations listC
Read-onlyIdempotent

Active Google Ads recommendations with their estimated conversion/cost impact.

Inputs: customer_id (required), limit, types. Returns: object

ads.report.ai_maxAds report ai maxC
Read-onlyIdempotent

AI Max matched-location-interest report.

Inputs: customer_id (required), date_range. Returns: object

ads.report.campaignAds report campaignD
Read-onlyIdempotent

Campaign performance report.

Inputs: customer_id (required), date_range. Returns: object

ads.report.conversionsAds report conversionsD
Read-onlyIdempotent

Conversions by conversion date.

Inputs: customer_id (required), date_range. Returns: object

ads.report.pmax_asset_groupsAds report pmax asset groupsC
Read-onlyIdempotent

Performance Max asset-group report.

Inputs: customer_id (required), date_range. Returns: object

ads.report.search_termsAds report search termsC
Read-onlyIdempotent

Search-terms report (incl. AI Max source).

Inputs: customer_id (required), date_range. Returns: object

ads.rollbackAds rollbackA
Destructive

Undo a Google Ads change from the signed audit trail (latest reversible change of this tenant, or audit_index): restores updated fields, removes created objects.

Inputs: confirm, audit_index. Returns: object

audit.anchorAudit anchorA

Anchor the audit chain head in Bitcoin via OpenTimestamps (only its SHA-256 leaves).

Inputs: no named parameters. Returns: object

audit.anchor_statusAudit anchor statusC
Read-onlyIdempotent

Upgrade pending OpenTimestamps proofs; report pending vs Bitcoin-confirmed anchors.

Inputs: no named parameters. Returns: object

audit.statusAudit statusC
Read-onlyIdempotent

Signed audit ledger: persistence, path, chain verification, public key.

Inputs: no named parameters. Returns: object

bigquery.dataset.createBigquery dataset createB

Create a dataset under a project (guarded: validate_only preview + confirm token).

Inputs: dataset_id (required), project_id (required), confirm. Returns: object

bigquery.datasets.listBigquery datasets listB
Read-onlyIdempotent

List datasets in a BigQuery project.

Inputs: project_id (required). Returns: object

bigquery.query.dry_runBigquery query dry runA
Read-onlyIdempotent

Dry-run a query: estimate scanned bytes + USD cost ($6.25/TiB), billing nothing.

Inputs: sql (required), project_id (required). Returns: object

bigquery.query.runBigquery query runB
Read-onlyIdempotent

Run a query only if it scans under max_bytes_billed (default 1e9 bytes).

Inputs: sql (required), project_id (required), max_bytes_billed. Returns: object

bigquery.table.createBigquery table createC

Create a table with a schema under a dataset (guarded).

Inputs: schema (required), table_id (required), dataset_id (required), project_id (required), +1 more. Returns: object

bigquery.tables.listBigquery tables listC
Read-onlyIdempotent

List tables in a BigQuery dataset.

Inputs: dataset_id (required), project_id (required). Returns: object

cm360.campaigns.listCm360 campaigns listB
Read-onlyIdempotent

List campaigns visible to a CM360 user profile.

Inputs: profile_id (required). Returns: object

cm360.floodlight_activities.listCm360 floodlight activities listB
Read-onlyIdempotent

List Floodlight activities (conversion tags) visible to a CM360 user profile.

Inputs: profile_id (required). Returns: object

cm360.placements.listCm360 placements listB
Read-onlyIdempotent

List placements (ad-serving slots) visible to a CM360 user profile.

Inputs: profile_id (required). Returns: object

cm360.placement.updateCm360 placement updateB
DestructiveIdempotent

Update a placement by id with a partial field set (guarded: preview + confirm token).

Inputs: fields (required), profile_id (required), placement_id (required), confirm. Returns: object

cm360.report.insertCm360 report insertA

Create a report definition from a report body (guarded: preview + confirm token).

Inputs: report (required), profile_id (required), confirm. Returns: object

cm360.report.runCm360 report runB
Read-onlyIdempotent

Run a report definition by id under a CM360 user profile (returns the report file).

Inputs: report_id (required), profile_id (required). Returns: object

cm360.reports.listCm360 reports listB
Read-onlyIdempotent

List report definitions visible to a CM360 user profile.

Inputs: profile_id (required). Returns: object

cm360.user_profiles.listCm360 user profiles listA
Read-onlyIdempotent

List the CM360 user profiles the authenticated user can access.

Inputs: no named parameters. Returns: object

creative.pmax_packCreative pmax packB
Read-onlyIdempotent

Generate a Performance Max pack: validated headlines, long headlines, descriptions, and images fitted to 1200x628 / 1200x1200.

Inputs: product (required), audience (required), scenes, language, +1 more. Returns: object

creative.search_adsCreative search adsB
Read-onlyIdempotent

Generate validated responsive-search-ad copy (15 headlines <=30, 4 descriptions <=90) with Gemini.

Inputs: product (required), audience (required), language, final_url. Returns: object

datamanager.audience.remove_membersDatamanager audience remove membersC
DestructiveIdempotent

Remove Customer Match audience members by hashed identifier (guarded).

Inputs: members (required), audience_id (required), destination_id (required), confirm. Returns: object

datamanager.audience.upload_membersDatamanager audience upload membersB

Upload Customer Match audience members (guarded). Identifiers must be SHA-256 hashed/normalized; consent (ad_user_data/ad_personalization) required for EEA.

Inputs: consent (required), members (required), audience_id (required), destination_id (required), +1 more. Returns: object

datamanager.conversions.uploadDatamanager conversions uploadB

Upload offline + enhanced conversions (guarded). Enhanced-conversion identifiers must be SHA-256 hashed/normalized; consent required for EEA.

Inputs: consent (required), conversions (required), destination_id (required), confirm. Returns: object

datamanager.request_statusDatamanager request statusC
Read-onlyIdempotent

Retrieve a Data Manager ingestion request's status (optional read backend).

Inputs: request_id (required). Returns: object

datamanager.statusDatamanager statusB
Read-onlyIdempotent

Report the Data Manager upload plane identity + operator contract (hashing/consent).

Inputs: no named parameters. Returns: object

describe_toolDescribe toolC
Read-onlyIdempotent

Describe a registered tool by name.

Inputs: name (required). Returns: object

dv360.advertisers.listDv360 advertisers listC
Read-onlyIdempotent

List advertisers under a DV360 partner.

Inputs: partner_id (required). Returns: object

dv360.campaigns.listDv360 campaigns listC
Read-onlyIdempotent

List campaigns under a DV360 advertiser.

Inputs: advertiser_id (required). Returns: object

dv360.insertion_orders.listDv360 insertion orders listC
Read-onlyIdempotent

List insertion orders under a DV360 advertiser.

Inputs: advertiser_id (required). Returns: object

dv360.line_item.set_statusDv360 line item set statusC
DestructiveIdempotent

Set a line item's entityStatus (activate/pause/archive) (guarded).

Inputs: line_item_id (required), advertiser_id (required), entity_status (required), confirm. Returns: object

dv360.line_items.listDv360 line items listC
Read-onlyIdempotent

List line items under a DV360 advertiser.

Inputs: advertiser_id (required). Returns: object

dv360.line_item.updateDv360 line item updateB
DestructiveIdempotent

Update a line item by id with a partial field set (guarded: preview + confirm token).

Inputs: fields (required), line_item_id (required), advertiser_id (required), confirm. Returns: object

ga4.admin.create_audienceGa4 admin create audienceC

Create an audience with filter clauses on a property (guarded).

Inputs: property_id (required), display_name (required), filter_clauses (required), confirm. Returns: object

ga4.admin.create_key_eventGa4 admin create key eventB

Create a key event (conversion) on a property (guarded: preview + confirm token).

Inputs: event_name (required), property_id (required), confirm. Returns: object

ga4.admin.list_data_streamsGa4 admin list data streamsC
Read-onlyIdempotent

List data streams under a GA4 property.

Inputs: property_id (required). Returns: object

ga4.admin.list_key_eventsGa4 admin list key eventsC
Read-onlyIdempotent

List key events (conversions) under a GA4 property.

Inputs: property_id (required). Returns: object

ga4.admin.list_propertiesGa4 admin list propertiesC
Read-onlyIdempotent

List GA4 properties under an Admin account.

Inputs: account_id (required). Returns: object

ga4.realtimeGa4 realtimeC
Read-onlyIdempotent

GA4 Data API realtime report (runRealtimeReport).

Inputs: metrics (required), dimensions (required), property_id (required). Returns: object

ga4.reportGa4 reportC
Read-onlyIdempotent

GA4 Data API report (runReport over a date range).

Inputs: metrics (required), end_date (required), dimensions (required), start_date (required), +1 more. Returns: object

gbp.accounts.listGbp accounts listA
Read-onlyIdempotent

List Google Business Profile accounts the authenticated user can access.

Inputs: no named parameters. Returns: object

gbp.location.getGbp location getB
Read-onlyIdempotent

Fetch a single GBP location's details by resource name.

Inputs: location_name (required). Returns: object

gbp.locations.listGbp locations listB
Read-onlyIdempotent

List locations under a Google Business Profile account.

Inputs: account_id (required). Returns: object

gbp.location.updateGbp location updateB
DestructiveIdempotent

Update a location's fields by resource name (guarded: preview + confirm token).

Inputs: fields (required), location_name (required), confirm. Returns: object

gbp.performance.fetchGbp performance fetchC
Read-onlyIdempotent

Fetch daily performance metrics (calls/directions/clicks/impressions) for a location.

Inputs: end_date (required), start_date (required), location_name (required). Returns: object

gbp.review.replyGbp review replyC
Destructive

Reply to a review by resource name (guarded: preview + confirm token).

Inputs: comment (required), review_name (required), confirm. Returns: object

gbp.reviews.listGbp reviews listA
Read-onlyIdempotent

List reviews for a location (legacy v4 host — requires Google allowlist approval).

Inputs: location_name (required). Returns: object

google.api.callGoogle api callA
Destructive

Run a write method of any listed Google API (GA4 Admin, GTM, Merchant, YouTube, DV360, CM360, Business Profile, Search Console, Sheets/Drive/Slides...). api is the short name from google.api.list (e.g. analyticsadmin), method the id from google.api.methods (e.g. properties.customDimensions.create), params only the URL path/query parameters (e.g. parent), body the request object. Contract-checked, guarded, audited, reversible where the API allows; deletes need allow_delete.

Inputs: api (required), method (required), body, params, +3 more. Returns: object

google.api.describeGoogle api describeC
Read-onlyIdempotent

One method's parameters (location, required, pattern, enum) and request body fields.

Inputs: api (required), method (required), version. Returns: object

google.api.listGoogle api listB
Read-onlyIdempotent

Google APIs reachable through the generic plane, with default versions.

Inputs: no named parameters. Returns: object

google.api.methodsGoogle api methodsC
Read-onlyIdempotent

Methods of a Google API from its Discovery contract (filter by resource, writes only).

Inputs: api (required), version, resource, writes_only. Returns: object

google.api.readGoogle api readC
Read-onlyIdempotent

Run a read-only method of any listed Google API, validated against its Discovery contract.

Inputs: api (required), method (required), params, version. Returns: object

google.api.rollbackGoogle api rollbackB
Destructive

Undo the latest reversible google.api.call of this tenant (or audit_index) from the audit.

Inputs: confirm, audit_index. Returns: object

gtm.create_tagGtm create tagB

Create a tag in a workspace (guarded: validate_only preview + confirm token).

Inputs: tag_name (required), tag_type (required), workspace_path (required), confirm, +1 more. Returns: object

gtm.create_versionGtm create versionC

Freeze a workspace into a new container version (guarded).

Inputs: name (required), workspace_path (required), confirm. Returns: object

gtm.list_accountsGtm list accountsB
Read-onlyIdempotent

List accessible GTM accounts.

Inputs: no named parameters. Returns: object

gtm.list_containersGtm list containersC
Read-onlyIdempotent

List containers under an account path.

Inputs: account_path (required). Returns: object

gtm.list_tagsGtm list tagsC
Read-onlyIdempotent

List tags under a workspace path.

Inputs: workspace_path (required). Returns: object

gtm.list_triggersGtm list triggersC
Read-onlyIdempotent

List triggers under a workspace path.

Inputs: workspace_path (required). Returns: object

gtm.list_variablesGtm list variablesB
Read-onlyIdempotent

List user-defined variables under a workspace path.

Inputs: workspace_path (required). Returns: object

gtm.list_versionsGtm list versionsB
Read-onlyIdempotent

List container version headers under a container path.

Inputs: container_path (required). Returns: object

gtm.list_workspacesGtm list workspacesC
Read-onlyIdempotent

List workspaces under a container path.

Inputs: container_path (required). Returns: object

gtm.publish_versionGtm publish versionC
DestructiveIdempotent

Publish a container version (guarded).

Inputs: version_path (required), confirm. Returns: object

gtm.update_tagGtm update tagC
DestructiveIdempotent

Update an existing tag by path (guarded).

Inputs: fields (required), tag_path (required), confirm. Returns: object

healthHealthB
Read-onlyIdempotent

Server + Google Ads API version health.

Inputs: no named parameters. Returns: object

insights.anomaliesInsights anomaliesA
Read-onlyIdempotent

Campaign anomalies: latest day vs each campaign's own history (robust z on cost, conversions, clicks).

Inputs: customer_id (required), days, threshold. Returns: object

insights.apply_negativesInsights apply negativesA
Destructive

Find wasted search terms (cost, no conversion) and add them as exact negative keywords in one guarded, audited write (undo with ads.rollback).

Inputs: customer_id (required), days, confirm, min_waste_cost_micros. Returns: object

insights.ga4_reconcileInsights ga4 reconcileB
Read-onlyIdempotent

Daily Google Ads conversions vs GA4 key events from google / cpc, with flagged gaps.

Inputs: customer_id (required), property_id (required), days. Returns: object

insights.pacingInsights pacingB
Read-onlyIdempotent

Month-to-date spend vs daily budgets, projected to month end; over/under-pacing campaigns.

Inputs: customer_id (required). Returns: object

insights.search_termsInsights search termsB
Read-onlyIdempotent

Search-term mining: wasted terms -> negative keywords, converting untargeted terms -> new keywords, each with a ready ads.mutate operation.

Inputs: customer_id (required), days, min_conversions, min_waste_cost_micros. Returns: object

language.analyze_entitiesLanguage analyze entitiesA
Read-onlyIdempotent

Extract salient entities from ad content. Incurs Cloud Natural Language API cost.

Inputs: text (required). Returns: object

language.analyze_sentimentLanguage analyze sentimentA
Read-onlyIdempotent

Analyze sentiment (score + magnitude) of ad content. Incurs Cloud Natural Language API cost.

Inputs: text (required). Returns: object

language.batch_translateLanguage batch translateA
Read-onlyIdempotent

Translate many texts into a target language in one call for multi-market reach. Incurs Cloud Translation API cost per text.

Inputs: texts (required), target_language (required), source_language. Returns: object

language.detect_languageLanguage detect languageB
Read-onlyIdempotent

Detect the dominant language of a text. Incurs Cloud Translation API cost.

Inputs: text (required). Returns: object

language.translateLanguage translateA
Read-onlyIdempotent

Translate text into a target language (auto-detects source if omitted). Incurs Cloud Translation API cost.

Inputs: text (required), target_language (required), source_language. Returns: object

list_capabilitiesList capabilitiesC
Read-onlyIdempotent

List connector capabilities and their tools.

Inputs: no named parameters. Returns: result

looker.dashboard.createLooker dashboard createB

Create a dashboard under a model (guarded: validate_only preview + confirm token).

Inputs: model (required), title (required), confirm. Returns: object

looker.dashboards.listLooker dashboards listB
Read-onlyIdempotent

List dashboards on the Looker instance.

Inputs: no named parameters. Returns: object

looker.look.runLooker look runB
Read-onlyIdempotent

Run a saved look by id (result_format defaults to json).

Inputs: look_id (required), result_format. Returns: object

looker.looks.listLooker looks listB
Read-onlyIdempotent

List saved looks on the Looker instance.

Inputs: no named parameters. Returns: object

looker.query.runLooker query runC
Read-onlyIdempotent

Run an inline model/view query with fields + filters.

Inputs: view (required), model (required), fields (required), filters (required). Returns: object

merchant.accounts.getMerchant accounts getC
Read-onlyIdempotent

Get a Merchant Center account.

Inputs: merchant_id (required). Returns: object

merchant.product.deleteMerchant product deleteC
DestructiveIdempotent

Delete a product by resource name (guarded).

Inputs: product_name (required), confirm. Returns: object

merchant.product.getMerchant product getC
Read-onlyIdempotent

Get a single product by its resource name.

Inputs: product_name (required). Returns: object

merchant.product.insertMerchant product insertB

Insert a product via productInput (guarded: validate_only preview + confirm token).

Inputs: merchant_id (required), product_input (required), confirm. Returns: object

merchant.products.listMerchant products listB
Read-onlyIdempotent

List products under a Merchant Center account.

Inputs: merchant_id (required). Returns: object

merchant.product_statuses.listMerchant product statuses listB
Read-onlyIdempotent

List item-level product statuses/issues for an account.

Inputs: merchant_id (required). Returns: object

merchant.product.updateMerchant product updateB
DestructiveIdempotent

Update a product by resource name with a partial field set (guarded).

Inputs: fields (required), product_name (required), confirm. Returns: object

meridian.budget.optimizeMeridian budget optimizeA
Read-onlyIdempotent

Optimal per-channel allocation for total_budget under a fitted MMM. Read-only (recommends only); may run posterior simulations.

Inputs: model_id (required), total_budget. Returns: object

meridian.fitMeridian fitA
Read-onlyIdempotent

Fit a Bayesian MMM on dataset_ref with config; returns a model/job handle. Read-only (no account mutation) but COMPUTE-HEAVY: MCMC sampling, minutes-to-hours.

Inputs: config (required), dataset_ref (required). Returns: object

meridian.model.summaryMeridian model summaryB
Read-onlyIdempotent

Fit-summary rows for an already-fitted MMM (by model_id). Read-only; cheap.

Inputs: model_id (required). Returns: object

meridian.roi.by_channelMeridian roi by channelB
Read-onlyIdempotent

Per-channel ROI / contribution rows for a fitted MMM (by model_id). Read-only.

Inputs: model_id (required). Returns: object

recaptcha.assessment.annotateRecaptcha assessment annotateC

Annotate an assessment LEGITIMATE/FRAUDULENT to improve the model (guarded).

Inputs: annotation (required), project_id (required), assessment_id (required), confirm. Returns: object

recaptcha.assessment.createRecaptcha assessment createB
Read-onlyIdempotent

Score a reCAPTCHA token: risk score 0.0-1.0 + reasons (the lead-quality signal).

Inputs: token (required), site_key (required), project_id (required), expected_action (required). Returns: object

recaptcha.keys.listRecaptcha keys listB
Read-onlyIdempotent

List the reCAPTCHA site keys configured under a project.

Inputs: project_id (required). Returns: object

sa360.conversion.uploadSa360 conversion uploadB

Upload SA360 conversions (guarded: preview + confirm token).

Inputs: conversions (required), customer_id (required), confirm. Returns: object

sa360.customers.list_accessibleSa360 customers list accessibleA
Read-onlyIdempotent

List SA360 customers (accounts) the authenticated user can access.

Inputs: no named parameters. Returns: object

sa360.report.ad_groupSa360 report ad groupC
Read-onlyIdempotent

Ad-group performance report (SA360).

Inputs: customer_id (required), date_range. Returns: object

sa360.report.campaignSa360 report campaignC
Read-onlyIdempotent

Campaign performance report (SA360).

Inputs: customer_id (required), date_range. Returns: object

sa360.searchSa360 searchB
Read-onlyIdempotent

Run an arbitrary read-only SA360 query (SA360 query language: SELECT ... FROM ...).

Inputs: query (required), customer_id (required). Returns: object

searchconsole.search_analyticsSearchconsole search analyticsC
Read-onlyIdempotent

Query Search Analytics (organic clicks/impressions/ctr/position) for a property.

Inputs: end_date (required), site_url (required), dimensions (required), start_date (required), +1 more. Returns: object

searchconsole.sitemaps.deleteSearchconsole sitemaps deleteC
DestructiveIdempotent

Delete a sitemap feed path for a property (guarded).

Inputs: feedpath (required), site_url (required), confirm. Returns: object

searchconsole.sitemaps.listSearchconsole sitemaps listB
Read-onlyIdempotent

List sitemaps submitted for a property.

Inputs: site_url (required). Returns: object

searchconsole.sitemaps.submitSearchconsole sitemaps submitA

Submit a sitemap feed path for a property (guarded: preview + confirm token).

Inputs: feedpath (required), site_url (required), confirm. Returns: object

searchconsole.sites.listSearchconsole sites listA
Read-onlyIdempotent

List Search Console properties the authenticated user can access.

Inputs: no named parameters. Returns: object

searchconsole.url_inspectSearchconsole url inspectC
Read-onlyIdempotent

Inspect a URL's index status for a property (URL Inspection API).

Inputs: site_url (required), inspection_url (required). Returns: object

trends.interest_by_regionTrends interest by regionC
Read-onlyIdempotent

Geographic breakdown of interest in a keyword (geo-targeting).

Inputs: keyword (required), resolution. Returns: object

trends.interest_over_timeTrends interest over timeB
Read-onlyIdempotent

Relative search interest over time for a comparison set (demand / seasonality).

Inputs: keywords (required), geo, timeframe. Returns: object

trends.related_queriesTrends related queriesB
Read-onlyIdempotent

Top + rising related search queries for a keyword (keyword expansion).

Inputs: keyword (required), geo. Returns: object

trends.trending_nowTrends trending nowB
Read-onlyIdempotent

Currently trending searches for a country (breakout demand).

Inputs: geo. Returns: object

vertex.gemini.generateVertex gemini generateB
Read-onlyIdempotent

Generate text with Gemini (creative copy/long-form). Incurs Vertex generation cost.

Inputs: prompt (required), model, max_tokens. Returns: object

vertex.gemini.generate_ad_copyVertex gemini generate ad copyC
Read-onlyIdempotent

Build a structured brief and ask Gemini for n ad-copy variants. Incurs Vertex cost.

Inputs: product (required), audience (required), n. Returns: object

vertex.imagen.generateVertex imagen generateA
Read-onlyIdempotent

Generate n images, returned as base64 (default model gemini-3.1-flash-image; pass model to override). Incurs Vertex cost per image.

Inputs: prompt (required), n, model, aspect_ratio. Returns: object

vertex.veo.generateVertex veo generateB
Read-onlyIdempotent

Generate a video clip with Veo 3.1 (returns an operation to poll with vertex.veo.status). Incurs Vertex cost.

Inputs: prompt (required), model, duration_seconds. Returns: object

vertex.veo.statusVertex veo statusB
Read-onlyIdempotent

Poll a Veo operation; when done, returns the generated video URIs/bytes. No cost.

Inputs: operation_name (required). Returns: object

video.generateVideo generateB
Read-onlyIdempotent

Generate a short video from a text prompt for free (Wan 2.2 -> LTX-Video on Hugging Face ZeroGPU; no billing). Returns the mp4 path (+ base64 on request). Daily GPU quota applies.

Inputs: prompt (required), seed, provider, aspect_ratio, +2 more. Returns: object

workspace.drive.list_filesWorkspace drive list filesB
Read-onlyIdempotent

List Drive files the authenticated user can access (optional files.list query).

Inputs: query. Returns: object

workspace.sheets.createWorkspace sheets createC

Create a new spreadsheet with a title (guarded).

Inputs: title (required), confirm. Returns: object

workspace.sheets.get_metadataWorkspace sheets get metadataB
Read-onlyIdempotent

Fetch spreadsheet metadata (title, sheet tabs, grid sizes).

Inputs: spreadsheet_id (required). Returns: object

workspace.sheets.read_rangeWorkspace sheets read rangeB
Read-onlyIdempotent

Read a cell range (A1 notation) from a spreadsheet.

Inputs: range_a1 (required), spreadsheet_id (required). Returns: object

workspace.sheets.write_rangeWorkspace sheets write rangeB
Destructive

Write values into an A1 range of a spreadsheet (guarded: preview + confirm token).

Inputs: values (required), range_a1 (required), spreadsheet_id (required), confirm. Returns: object

workspace.slides.create_deckWorkspace slides create deckC

Create a new Slides deck with a title (guarded).

Inputs: title (required), confirm. Returns: object

youtube.analytics.queryYoutube analytics queryC
Read-onlyIdempotent

Query organic video performance (views/watch time/etc.) from YouTube Analytics.

Inputs: ids (required), metrics (required), end_date (required), dimensions (required), +1 more. Returns: object

youtube.channel.getYoutube channel getB
Read-onlyIdempotent

Get a channel's snippet/statistics + uploads-playlist id (Data API v3).

Inputs: channel_id (required). Returns: object

youtube.playlist.add_itemYoutube playlist add itemC

Add a video to a playlist (playlistItems.insert) — guarded.

Inputs: video_id (required), playlist_id (required), confirm. Returns: object

youtube.playlist_items.listYoutube playlist items listB
Read-onlyIdempotent

Enumerate playlist items (use the uploads playlist; avoid quota-scarce search.list).

Inputs: playlist_id (required). Returns: object

youtube.reporting.ensure_jobsYoutube reporting ensure jobsC
Read-onlyIdempotent

Ensure Reporting API bulk-report jobs exist (reports retained 30/60d — persist early).

Inputs: report_type_ids (required). Returns: object

youtube.videos.listYoutube videos listB
Read-onlyIdempotent

List full video resources for a batch of video ids (Data API v3).

Inputs: video_ids (required). Returns: object

youtube.video.updateYoutube video updateB
DestructiveIdempotent

Update video metadata (title/description/tags) — guarded; no media upload.

Inputs: fields (required), video_id (required), confirm. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.5/5.0

    Scored across 163 tools

    Disambiguation2/5

    Several tools have overlapping boundaries: google.api.read/call can duplicate platform-specific list/write tools, ads.mutate overlaps with ads.campaign.create_* and ads.budget.update/set_status, and multiple ad-copy/video generators serve similar goals. Descriptions help, but the generic planes create real misselection risk across a 163-tool surface.

    Naming Consistency3/5

    Most tools use lowercase dot-separated namespaces such as merchant.product.insert or ga4.admin.list_properties, but the pattern varies: gtm.list_accounts, youtube.channel.get, looker.dashboards.list, and unprefixed tools like health/list_capabilities/describe_tool. The naming is still readable, but it is not one predictable convention.

    Tool Count1/5

    At 163 tools, this is far beyond a well-scoped tool set and is extremely difficult for an agent to navigate reliably. Even if each tool serves a broad multi-platform ads suite, the count represents extreme over-provisioning.

    Completeness4/5

    The surface covers core read, write, reporting, analytics, and audit workflows across Google Ads, DV360, CM360, SA360, GA4, GTM, Merchant, Search Console, YouTube, BigQuery, and more, often with a generic Google API fallback. Some platform-specific CRUD or lifecycle operations are thin, such as DV360/CM360 create paths, but agents can generally work around them.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      A
      maintenance
      Google Ads, Reddit Ads, GA4, Tag Manager, Search Console and Merchant Center in your AI client. Read reports and make changes with a preview, a dry run validated by Google, and budget and bid caps before anything goes live. Self-host (MIT, pip install adloop) or connect AdLoop Cloud at https://mcp.getadloop.com/mcp.
      153
      2,317 PyPI
      297
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to manage, report, and analyze Google Ads campaigns securely with encrypted multi-client support, real-time API integrations, and audit trail logging.
      69 npm
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      A Model Context Protocol server providing full read and write control over Google Ads, enabling AI assistants to build campaigns, fix targeting, rewrite ads, and manage bidding strategies without opening a browser.
      1
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables self-hosted Google Ads and Meta Ads management through MCP, with human approval, hard spending limits, and audited writes; agents like Claude Code or Codex can connect to ad accounts while the server controls all credential access and platform writes.
      Apache 2.0