demand-gen-audit — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited demand-gen-audit (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A deep audit of your Demand Gen campaigns — Google's visual, social-style campaign type running across Discover, Gmail, YouTube feeds and Shorts. Demand Gen lives or dies on audiences and creative, so that's where this audit spends its time.
Requires: Cogny Agent subscription ($9/mo) — Sign up
| Job | Primary | Also answers it | Differences to state |
|---|---|---|---|
| Audiences, creative formats, ad strength | Google Ads | — | Google-only levers |
| Same-job campaign totals | Google Ads (Demand Gen) | Microsoft Ads (Bing_Ads, campaign type Audience) | Bing money is account-currency units; Microsoft Audience Network inventory, not YouTube |
| Post-click quality | Google Analytics 4 | — | GA4 engaged sessions and key events per campaign; GA4 attribution ≠ Ads |
| Prospecting CPA benchmark | — | Meta Ads (tool_get_insights) | Meta counts link clicks and conversions differently; compare CPA, never sum |
Call list_integrations once. Everything beyond Google Ads is optional context.
If no mcp__cogny__* tools exist (no list_integrations, no cogny_status), print this and stop:
Demand Gen Audit needs your marketing data through Cogny's MCP gateway.
1. Sign up at https://cogny.com and connect Google Ads (plus Microsoft Ads, GA4 or Meta Ads
if you use them).
2. Add Cogny to .mcp.json in your project:
{"mcpServers": {"cogny": {"type": "http", "url": "https://app.cogny.com/mcp"}}}
3. Restart Claude Code; it opens a browser for the OAuth login on first use.
Nothing to run without Cogny.If Cogny is connected but mcp__cogny__mcp__Google_Ads__tool_execute_gaql is missing, say "connect Google Ads in Cogny" (or "reconnect Google Ads in Cogny" if it's listed but unhealthy or a read returns 401/permission denied) and stop.
/demand-gen-audit — audit every Demand Gen campaign in the account /demand-gen-audit Prospecting - Q2 — audit one campaign by name or id
Demand Gen has no keywords and no search intent. It's an interruption channel — you win by showing the right audience the right creative. The audit checks audience strategy, creative format spread, creative fatigue, and whether CPA is tracking toward goal.
Google Ads money fields are micros (divide by 1,000,000). An empty GAQL result (no rows, or the tool returns no output) means zero: no matching entities or no activity in the window. Report it as 0, not as an error.
SELECT campaign.id, campaign.name, campaign.status,
campaign.bidding_strategy_type, campaign.target_cpa.target_cpa_micros,
campaign_budget.amount_micros, metrics.cost_micros, metrics.conversions,
metrics.conversions_value, metrics.clicks, metrics.impressions
FROM campaign
WHERE campaign.advertising_channel_type = 'DEMAND_GEN'
AND segments.date DURING LAST_30_DAYS
ORDER BY metrics.cost_micros DESCIf this returns no rows, also check for paused or idle ones (same query without the date filter, campaign.status != 'REMOVED'). If there are still none, report "no Demand Gen campaigns in this account" and stop; suggest /google-ads-audit for the account as a whole.
Compute CPA and compare to the campaign's target CPA. Flag campaigns running >50% over target.
Microsoft Ads (if connected): tool_get_campaigns (account_id, campaign_types: ["Audience"]) and tool_get_report (account_id from tool_list_accounts, report_type: "campaign", campaign_ids). Show Microsoft Audience campaigns as their own row block with CPA in the account's currency.
SELECT ad_group.name, ad_group.status, campaign.name,
metrics.cost_micros, metrics.conversions, metrics.clicks
FROM ad_group
WHERE campaign.advertising_channel_type = 'DEMAND_GEN'
AND segments.date DURING LAST_30_DAYSSELECT ad_group.name, ad_group_criterion.type,
ad_group_criterion.user_list.user_list,
ad_group_criterion.audience.audience
FROM ad_group_criterion
WHERE campaign.advertising_channel_type = 'DEMAND_GEN'
AND ad_group_criterion.status = 'ENABLED'Flag:
visitors) and no lookalike segments
creative and different bids
SELECT ad_group.name, ad_group_ad.ad.id, ad_group_ad.ad.type,
ad_group_ad.ad_strength, ad_group_ad.status,
metrics.impressions, metrics.clicks, metrics.conversions
FROM ad_group_ad
WHERE campaign.advertising_channel_type = 'DEMAND_GEN'
AND segments.date DURING LAST_30_DAYSDemand Gen ad types: single image, carousel, and video responsive. Flag:
add video
POOR / AVERAGEPull each enabled ad for the last 30 days, then for the prior 30 (the 30 days before LAST_30_DAYS, as a BETWEEN range):
SELECT campaign.name, ad_group.name, ad_group_ad.ad.id, ad_group_ad.ad.type,
metrics.impressions, metrics.clicks, metrics.ctr, metrics.conversions
FROM ad_group_ad
WHERE campaign.advertising_channel_type = 'DEMAND_GEN'
AND ad_group_ad.status = 'ENABLED'
AND segments.date DURING LAST_30_DAYSSELECT campaign.name, ad_group.name, ad_group_ad.ad.id, ad_group_ad.ad.type,
metrics.impressions, metrics.clicks, metrics.ctr, metrics.conversions
FROM ad_group_ad
WHERE campaign.advertising_channel_type = 'DEMAND_GEN'
AND ad_group_ad.status = 'ENABLED'
AND segments.date BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD'Join on ad_group_ad.ad.id. An ad present in the prior window was already live 30+ days ago; compare CTR and conversion rate between the windows (ignore ads under ~1,000 impressions in either window). Flag:
Spend by surface:
SELECT campaign.name, segments.ad_network_type, metrics.impressions, metrics.clicks,
metrics.cost_micros, metrics.conversions
FROM campaign
WHERE campaign.advertising_channel_type = 'DEMAND_GEN'
AND segments.date DURING LAST_30_DAYSTop placements, where Google reports them:
SELECT campaign.name, detail_placement_view.display_name,
detail_placement_view.placement_type, detail_placement_view.target_url,
metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions
FROM detail_placement_view
WHERE campaign.advertising_channel_type = 'DEMAND_GEN'
AND segments.date DURING LAST_30_DAYS
ORDER BY metrics.cost_micros DESC
LIMIT 50Google exposes little placement detail for Demand Gen, so this second query is often empty even when the campaign served. Then judge from the surface split above, and mark the exclusion check manual: "Check Tools → Content suitability and the campaign's placement report in the Google Ads UI." Don't claim exclusions are missing from an empty result.
Flag:
surface-specific campaigns)
Demand Gen is upper/mid funnel. Flag campaigns optimizing to a hard bottom-funnel action (purchase) with too few conversions to learn — a softer goal (lead, signup, add-to-cart) often suits the funnel stage better.
tool_list_account_summaries for the property, then tool_run_report (property_id, date_ranges,dimensions: ["sessionGoogleAdsCampaignName"], metrics: ["sessions", "engagementRate", "keyEvents"]). A Demand Gen campaign with cheap clicks but an engagement rate far below the site's paid-search average is buying accidental taps (often Shorts/Discover placements); that supports a placement finding.
tool_list_ad_accounts, then tool_get_insights (ad_account_id,level: "campaign", time_range_since, time_range_until) for prospecting campaigns. Put the Meta prospecting CPA next to Demand Gen's as context for budget decisions. Different conversion definitions: label both, don't declare a winner on CPA alone.
Demand Gen Audit — [Account Name]
Demand Gen Score: X/100 · 30-day spend: [X] · Conv: [X] · CPA vs target: [X]
| Campaign | Ad groups | Audiences | Formats | Avg ad strength | CPA |
|----------|-----------|-----------|---------|-----------------|-----|
🔴 Critical — no audiences, no conversions, CPA far over target
🟡 Important — single-format creative, fatigue, no first-party signal
🟢 Optimization — add lookalikes, placement exclusions, creative refresh
Top 3 Actions:
1. [Highest $ impact]
2. ...
3. ...{
"title": "Demand Gen 'Prospecting - Q2' runs image-only — missing YouTube & Shorts",
"body": "All 4 ads in the campaign are single-image. Demand Gen serves video inventory on YouTube feed and Shorts that image ads can't reach. The campaign spends $110/day. Add 2-3 vertical video creatives to open that inventory.",
"action_type": "creative_refresh",
"expected_outcome": "Access to video inventory; lower CPM and broader reach",
"estimated_impact_usd": 900,
"priority": "medium"
}Cloud uses create_ticket with the same content, fields mapped per section 2 of the routing guide.
Action types: creative_refresh, audience_refinement, campaign_optimization, bidding_strategy, placement_exclusion.
Use create_ticket when the workspace has it (Cloud) and create_finding otherwise (Solo). The fields differ; see section 2 of ../marketing-engineer/references/mcp-routing.md.
spend the audit there.
(tool_add_audience_to_ad_group, tool_update_ad_status) are used only on the user's request, and the skill asks first.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.