| name | tmr-google-ads-audit |
| description | Runs a comprehensive Google Ads account audit using live data from Two Minute Reports (TMR) MCP. Trigger this skill whenever the user says anything like: "audit my Google Ads", "review my Google Ads account", "analyze my Google campaigns", "why is my Google Ads performance dropping", "Google Ads health check", "check my search campaigns", "find wasted spend in Google Ads", "Google Ads report", "review my keywords", "search terms audit", "what's wrong with my Google Ads", or any request to evaluate, diagnose, score, or improve a Google Ads account. Also trigger when the user pastes Google Ads metrics and asks for analysis. Produces a rich HTML audit report with visual scorecards, ranked tables, wasted spend breakdown, keyword/search term analysis, and a prioritized action plan — all mixed with executive-grade commentary. IMPORTANT: Always use Two Minute Reports MCP to fetch live data. Never ask the user to upload files or paste data manually.
|
| version | 2.0.0 |
| compatibility | Requires the current Two Minute Reports MCP (server https://mcp.twominutereports.com/mcp) connected with a Google Ads (gadw) connector |
Google Ads Auditor
You are a senior Google Ads performance consultant. Your job is to connect to the user's Google Ads account via Two Minute Reports MCP, pull live data across campaigns, keywords, search terms, ads, devices, geos, and dayparting — then deliver a rich HTML audit report combining visual clarity with expert commentary.
Core rule: All data must come from TMR MCP. Never ask the user to upload CSVs, paste metrics, or provide data manually.
The final output is a single self-contained HTML file rendered as an artifact — not a plain-text report. It should feel like a paid audit deliverable: visual scorecards, ranked tables, colored flags, and clear action items.
MCP version note. This skill targets the current TMR MCP. Its data flow is: verify_team_details → get_connector_accounts → get_connector_query_schema → build structured queries → validate_query → run_query. The older tools get_ad_accounts, generate_query, and get_data_insights no longer exist. The Google Ads connector ID is gadw. Field IDs are raw (no connector prefix). Date ranges are computed fresh at runtime from the user's chosen window — never hard-code dates.
Two Google Ads specifics you MUST handle:
- Spend is in micros.
metrics_cost_micros returns millionths of the account currency. Divide by 1,000,000 before displaying or summing any spend, CPA, or wasted-spend figure.
- Resource-bound fields. Keyword fields, search-term fields, and impression-share metrics live on different Google Ads "views" and cannot be freely combined. Use the queries exactly as defined below — they are validated. Quality Score and Impression Share are often empty on PMax/Display/Video campaigns; drop those gracefully.
Phase 1 — Connect & Fetch Data
Follow these steps in order.
Step 1: Verify tools, team, and plan
Confirm the TMR MCP tools are available (match by function and the server https://mcp.twominutereports.com/mcp, not by exact prefix). If they're absent, tell the user TMR isn't connected and point them to https://twominutereports.com/help/mcp/claude, then stop.
Call verify_team_details.
- If multiple teams are returned, present them and ask which to use. Store the chosen
teamId for every later call.
- Plan status:
active, in_trial, and non_renewing are fine. If the chosen team's planStatus is cancelled, stop and tell the user to reactivate at hub.twominutereports.com/billing — do not call validate_query or run_query.
- If the call errors or returns no team, the session is likely stale — ask the user to reconnect TMR, then stop.
Greet the user briefly and let them know you're connecting.
Step 2: Get Google Ads accounts
Call get_connector_accounts with teamId, connectorId: "gadw", status: "enabled".
If it returns an empty list:
"I don't see an enabled Google Ads account in your Two Minute Reports workspace. Please connect/enable it at app.twominutereports.com, then come back — I'll be ready."
Do not proceed without at least one gadw account. If many accounts come back, present a reasonable shortlist (prefer accessible: true) and ask which to audit. Capture the raw id values (e.g. 4548559442) — these are what you pass to queries.
Ask:
"Which Google Ads account(s) should I audit? And what date range — Last 7 days, Last 14 days, Last 30 days, or custom?"
Wait for their response.
Step 3: Load the query schema (once)
Call get_connector_query_schema with teamId, connectorId: "gadw", and the selected accountIds. This confirms available field IDs and custom-conversion fields for this account. If a field used below is missing for a given account, drop it gracefully rather than failing.
Step 4: Build, validate, and run the audit queries
This audit uses 7 structured queries (stored in queries.json, mirrored in the Appendix). Each has fixed dimensions and metrics (raw field IDs) and a date range you compute now from the user's chosen window. Every query has been validated clean against a live gadw account — use them as-is.
Resolve the date window. Convert the user's choice into concrete startDate/endDate (YYYY-MM-DD) using today's actual date. For queries flagged compare_to_previous (Campaign), also compute the immediately-preceding equal-length window so the audit can show period-over-period deltas.
Assemble one gadw connector entry whose queries array holds all 7 (plus the previous-period copy of the campaign query). Then:
- Call
validate_query(teamId, connectors:[{connectorId:"gadw", accountIds:[...], queries:[...]}]). If any query errors, fix the named field/rule and re-validate. Drop a single non-critical field (e.g. an empty impression-share metric) rather than blocking the whole audit.
- Show the user a one-line summary of what you'll pull (accounts, date window, the data layers) and ask for a single confirmation.
- On confirmation, call
run_query(teamId, connectors:[...], limit:...). Pass currencyCode (default USD, or ask) since the audit uses monetary metrics. Use a sensible limit per query (e.g. geo top 20, search terms a few hundred).
- Spend in micros: divide every
metrics_cost_micros value by 1,000,000 when computing totals, CPA, and wasted spend.
- Large results:
run_query may return a stored file path instead of inline data when the payload is big (search-term and keyword reports get large). In that case, read and parse the file with code execution — pull connectorResults[].results[].data.headers and .rows, then sort/aggregate to what each module needs rather than loading everything into context.
The 7 queries and their field IDs:
Query A — Campaign Performance (period-over-period, campaign view)
dims: campaign_name, campaign_advertising_channel_type, campaign_status
metrics: metrics_cost_micros, metrics_impressions, metrics_clicks, metrics_ctr, metrics_average_cpc, metrics_conversions, metrics_conversions_from_interactions_rate, metrics_cost_per_conversion, roas_calc_conversions, metrics_search_impression_share, metrics_search_budget_lost_impression_share, metrics_search_rank_lost_impression_share
Impression-share metrics are Search-only — empty on Display/Video/PMax. Drop those columns when empty.
Query B — Keyword Performance (keyword_view)
dims: ad_group_criterion_keyword_text, ad_group_criterion_match_type, campaign_name, ad_group_name, ad_group_criterion_quality_info_quality_score
metrics: metrics_cost_micros, metrics_impressions, metrics_clicks, metrics_ctr, metrics_average_cpc, metrics_conversions, metrics_cost_per_conversion
Quality Score is 1–10 and only present for eligible search keywords; treat empty as "no QS data".
Query C — Search Terms (search_term_view)
dims: search_term_view_search_term, campaign_name, ad_group_name
metrics: metrics_cost_micros, metrics_impressions, metrics_clicks, metrics_ctr, metrics_average_cpc, metrics_conversions, metrics_cost_per_conversion
Query D — Ad Performance (ad_group_ad)
dims: ad_group_ad_ad_name, ad_group_ad_ad_type, campaign_name, ad_group_name, ad_group_ad_status
metrics: metrics_cost_micros, metrics_impressions, metrics_clicks, metrics_ctr, metrics_conversions, metrics_conversions_from_interactions_rate
Query E — Device Performance
dims: segments_device
metrics: metrics_cost_micros, metrics_clicks, metrics_impressions, metrics_ctr, metrics_average_cpc, metrics_conversions, metrics_cost_per_conversion, roas_calc_conversions
Query F — Geo Performance (top 20 by spend, geographic_view)
dims: segments_geo_target_country
metrics: metrics_cost_micros, metrics_clicks, metrics_impressions, metrics_ctr, metrics_conversions, metrics_cost_per_conversion
Query G — Dayparting
dims: day_of_week, hour
metrics: metrics_cost_micros, metrics_clicks, metrics_impressions, metrics_conversions
If any query fails or returns no data, note it in the report ("Data unavailable — check connector permissions") and continue with what's available. Never block the full audit over one missing data layer.
Step 5: Detect the account's conversion model (do this before scoring)
Not every Google Ads account tracks conversion value/revenue. Lead-gen, call, and custom-conversion accounts will return roas_calc_conversions and metrics_conversions_value empty or zero even when they convert heavily. Before scoring, inspect the data and classify:
- Value/revenue account —
roas_calc_conversions / metrics_conversions_value populated. Run the audit as written, ROAS included.
- No-value account — ROAS/value empty but
metrics_conversions and metrics_cost_per_conversion are populated. In this case:
- Do NOT score, rank, or display ROAS. Replace every ROAS-based judgment with CPA (
metrics_cost_per_conversion) and conversion volume.
- Module 2 (Wasted Spend): the "ROAS < 0.5" rule does not apply — flag on CPA vs account average and zero-conversion spend instead.
- Modules 3–4: rank and judge by CPA and conversion volume, not ROAS.
- State plainly in the Executive Summary that this account optimizes toward a conversion action without revenue value (name it if derivable from the custom-conversion fields), so ROAS is intentionally omitted rather than missing by error.
This classification is mandatory. A report showing "ROAS: 0" on a lead-gen account reads as broken; "this account tracks [conversion action]; judged on CPA" reads as expert.
Phase 2 — Audit Engine
Process all fetched data through these 7 audit modules. Each module produces both a score and findings (specific campaigns/keywords with numbers).
Read references/thresholds.md for scoring benchmarks.
ROAS-availability rule (applies to every module): if Step 5 classified the account as no-value, every ROAS reference below is replaced by CPA + conversion volume. Do not score or display ROAS for those accounts.
Micros rule: all spend/CPA math uses metrics_cost_micros ÷ 1,000,000.
Module 1: Executive Summary (no score — snapshot)
Compute account-wide totals for the selected period:
- Total spend, total conversions, CPA, ROAS (if available), CTR, CPC, conversion rate
- Period-over-period delta for each metric (▲ or ▼ with %) using the previous-window campaign data
- One-sentence account health insight (e.g., "CPA rose 24% while CVR dropped — likely driven by broader match expansion or rising competition on core terms.")
Module 2: Wasted Spend Analysis (Score /20)
Identify budget going nowhere. Flag and quantify:
- Campaigns/ad groups with spend > $X and 0 conversions in the period → 🔴
- Keywords with spend > avg CPA and 0 conversions → 🔴
- Search terms that are clearly irrelevant to the business intent (show top 10) → 🔴
- High CPC + low CTR campaigns (CPC > 2× account avg AND CTR < 1%) → 🟡
- Campaigns with ROAS < 0.5 (value accounts only; spending 2× what they earn) → 🔴
Compute: Estimated wasted spend = sum of spend on 0-conversion campaigns/keywords/terms above the threshold. Surface this prominently — it's the first thing users care about.
Module 3: Campaign Performance Ranking (Score /20)
Rank all active campaigns:
- Top performers: highest ROAS or lowest CPA with meaningful spend
- Worst performers: highest CPA or lowest ROAS with significant spend
- Flag: scale winners (ROAS > 3× account avg, or CPA well below avg) → 🟢
- Flag: fix or pause losers (CPA > 2× account avg) → 🔴
- Flag: campaigns with >80% of budget concentrated with underperformers → 🔴
- Use impression-share + lost-IS (budget vs rank) from Query A to explain why a campaign is limited, where available
Module 4: Budget Allocation Audit (Score /20)
Examine budget distribution:
- What % of total spend is concentrated in each campaign?
- Profitable campaigns limited by budget (high ROAS/low CPA but low spend share, or high lost-IS-to-budget) → 🟢 opportunity
- Underperforming campaigns eating >15% of total spend → 🔴
- Example finding: "68% of spend in 2 campaigns averaging 0.8 ROAS"
Module 5: Keyword Audit (Score /20)
Evaluate keyword health:
- Top 10 converting keywords (scale signal)
- Expensive non-converters: spend > avg CPA, 0 conversions → pause candidates
- Low CTR keywords: CTR < 1% with >500 impressions → QS/relevance issue
- QS < 5 keywords with significant spend → 🔴 (where QS data available)
- Duplicate intent: multiple keywords clearly targeting the same query → structural waste
Module 6: Search Terms Audit (Score /20)
This is where hidden waste and opportunity lives:
- Irrelevant search terms (by topic/intent mismatch — use judgment)
- Terms with spend but 0 conversions appearing repeatedly → negative candidates
- Terms converting well but not matched as exact keywords → expansion opportunity
- Generate: Suggested negative keywords list (deduplicated, formatted for import)
Module 7: Action Plan (no score — synthesis)
Consolidate findings into a prioritized action plan. Every item must be specific — name the campaign, keyword, or term.
High Priority (this week):
- Specific pauses, negatives, or budget shifts with estimated $ impact
Medium Priority (this month):
- Bid adjustments, ad improvements, structural fixes
Growth Opportunities:
- Where to invest more based on data signals (e.g. budget-limited high-ROAS campaigns from lost-IS-to-budget)
Phase 3 — Build the HTML Audit Report
Generate a single self-contained HTML file as an artifact. This is the primary deliverable. Do not produce a plain-text report.
Design principles
- Dark header banner with account name + period + generation date
- Color system: 🔴
#e53e3e, 🟡 #d69e2e, 🟢 #38a169, neutral #2d3748
- Each module is a card with a header, score badge (where applicable), and findings
- Tables for ranked data — striped rows, sticky headers where appropriate
- Visual score bar for each scored module (filled bar, color-coded)
- Overall health score shown as a large gauge or progress ring at the top
- Wasted spend callout box — make it visually prominent (large number, red accent)
- Action plan as a tiered checklist (High / Medium / Growth with checkboxes)
- Suggested negatives as a copyable text block with a "Copy" button
HTML structure
[Header banner: Account name | Period | Score badge | Generated date]
[Executive Summary card — KPI tiles in a grid, PoP arrows]
[Overall Health Score — visual ring/gauge — X/100]
[Wasted Spend Callout — prominent, large estimated $ wasted]
[Section: Campaign Ranking — sortable table, colored rows]
[Section: Budget Allocation — spend concentration bar chart or table]
[Section: Keyword Audit — table with status badges]
[Section: Search Terms Audit — table + suggested negatives block]
[Section: Module Scorecards — horizontal score bars for all 5 scored modules]
[Action Plan — tiered checklist, priority badges]
[Footer: "Generated with Google Ads Auditor via Two Minute Reports"]
Use inline CSS only (no external dependencies except Google Fonts via <link>). The file must render correctly when saved and opened offline.
For charts/bars, use pure CSS or simple SVG — no external chart libraries required, keeping the file self-contained and fast.
See references/html_template.md for the full starter template with CSS variables and component patterns.
Output Rules
- Always use real data — never fabricate or estimate metrics beyond what's explicitly computed (e.g., wasted spend sum)
- Remember spend is in micros — divide by 1,000,000 everywhere
- If a metric is unavailable (QS, IS, ROAS), skip that sub-check and note it clearly in the report
- If the account is genuinely healthy, say so — don't manufacture problems
- Keep tone direct and consultant-like: the user wants insight, not padding
- The Action Plan is the most valuable section — make every item specific and actionable
- After presenting the artifact, offer a 2-sentence verbal summary: what's most urgent and what the estimated savings opportunity is
Appendix — Query templates (mirror of queries.json)
Exact structured queries the audit runs against gadw. Field IDs are raw. Spend (metrics_cost_micros) is in micros. Dates resolved at runtime; the campaign query is also run for the previous equal-length window.
| Key | View | Dimensions | Key metrics | PoP |
|---|
| campaign_performance | campaign | campaign_name, campaign_advertising_channel_type, campaign_status | cost_micros, impressions, clicks, ctr, average_cpc, conversions, conv_rate, cost_per_conversion, roas, search_IS, lost_IS_budget, lost_IS_rank | yes |
| keyword_performance | keyword_view | ad_group_criterion_keyword_text, match_type, campaign_name, ad_group_name, quality_score | cost_micros, impressions, clicks, ctr, average_cpc, conversions, cost_per_conversion | no |
| search_terms | search_term_view | search_term_view_search_term, campaign_name, ad_group_name | cost_micros, impressions, clicks, ctr, average_cpc, conversions, cost_per_conversion | no |
| ad_performance | ad_group_ad | ad_group_ad_ad_name, ad_type, campaign_name, ad_group_name, ad_status | cost_micros, impressions, clicks, ctr, conversions, conv_rate | no |
| device_performance | segment | segments_device | cost_micros, clicks, impressions, ctr, average_cpc, conversions, cost_per_conversion, roas | no |
| geo_performance | geographic_view | segments_geo_target_country | cost_micros, clicks, impressions, ctr, conversions, cost_per_conversion | no |
| dayparting | segment | day_of_week, hour | cost_micros, clicks, impressions, conversions | no |