npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@metaharness/example-ads

v0.2.0

Published

MetaHarness example — Google Ads + Meta Marketing API harness, campaign/spend analysis read-only by default, mutations gated (npx @metaharness/example-ads my-bot)

Downloads

324

Readme

@metaharness/example-ads

Multi-agent scaffold for Google Ads + Meta Marketing API — campaign and spend analysis, read-only by default, mutations gated.

⚠️ Illustrative output. Transcripts, metric values, and validation output shown in this README are representative examples, not captured from a specific run. Actual output depends on your environment, models, credentials, and ad account data. Run the commands to see real results.

⚠️ Not for production advertising operations. This scaffold is illustrative and educational. It is NOT certified for compliance with GDPR, CCPA, Google Ads API Terms of Service, Meta Platform Terms, or any other advertising regulation. All mutations are the sole responsibility of the operator. Always test with a sandbox / test account before connecting production credentials.

npm version npm downloads License: MIT Node >=20 built with metaharness


What it is

@metaharness/example-ads is a one-command scaffold that wires a three-agent harness to Google Ads (google-ads-api, community library by Opteo) and Meta Marketing API (facebook-nodejs-business-sdk v25, official Meta SDK). Out of the box it can only read — campaign lists, spend breakdowns, performance metrics, cross-channel summaries. Any write operation (budget change, campaign pause, ad creation) requires an explicit environment-variable opt-in and is still run through a validate_only dry-run before the live call.

What it is NOT

  • Not a production advertising management system.
  • Not a replacement for native ad managers (Google Ads UI, Meta Ads Manager).
  • Not GDPR / CCPA compliant out of the box.
  • Not affiliated with or endorsed by Google or Meta.
  • google-ads-api is a community library — Google has no official Node.js client. This scaffold makes that clear in generated code comments.

Features

| Capability | Detail | |---|---| | Campaign spend analysis | GAQL query for metrics.cost_micros, metrics.impressions, metrics.clicks, metrics.conversions at campaign / ad-group level for any date range | | Meta Insights fan-out | AdAccount.getInsights() at campaign, adset, or ad level; fields: spend, impressions, clicks, cpc, cpm, reach, roas | | Cross-channel summary | Planner agent synthesises Google + Meta results into a unified spend dashboard | | Budget-pacing alert | Detects campaigns on pace to over- or under-spend their daily/lifetime budget | | Tiered model routing | Tier 2 (cheap/fast) for fan-out data extraction; Tier 3 (frontier) for cross-channel synthesis and anomaly narrative | | MCP default-deny | .harness/mcp-policy.json grants only the minimum read tools; all mutation tools blocked unless ADS_MUTATIONS_ENABLED=true | | /ads-analyze slash command | Single entry-point for analysis across both platforms | | Three specialised agents | ads-planner (Tier 3), ads-fetcher (Tier 2), ads-verifier (Tier 2) | | Verification gate | ads-verifier re-reads a random sample of metrics independently before the planner surfaces results | | All hosts | --host flag scaffolds for claude-code, codex, copilot, github-actions, hermes, openclaw, opencode, pi-dev, rvm; --host all emits every config | | Audit log | Every API call written to .harness/audit.jsonl |


Quickstart

npx @metaharness/example-ads@latest my-ads-bot
cd my-ads-bot
npm install
npm run doctor

npm run doctor checks that required environment variables are set, that both SDKs are reachable, and that the MCP policy file is intact.

To scaffold for a different host:

npx @metaharness/example-ads@latest my-ads-bot --host opencode
# or emit every host config at once:
npx @metaharness/example-ads@latest my-ads-bot --host all

Configuration

Google Ads credentials

| Env var | Where to get it | Required | |---|---|---| | GOOGLE_ADS_CLIENT_ID | Google Cloud Console → APIs & Services → Credentials → OAuth 2.0 Client | Yes | | GOOGLE_ADS_CLIENT_SECRET | Same OAuth 2.0 client entry | Yes | | GOOGLE_ADS_DEVELOPER_TOKEN | Google Ads Manager account → Tools → API Center | Yes | | GOOGLE_ADS_REFRESH_TOKEN | Run the OAuth flow; npm run get-google-token in the scaffolded project prints instructions | Yes | | GOOGLE_ADS_CUSTOMER_ID | Your Google Ads account ID, no hyphens (e.g. 1234567890) | Yes | | GOOGLE_ADS_LOGIN_CUSTOMER_ID | Manager account ID — required only when accessing sub-accounts via a manager | Conditional |

Meta Marketing API credentials

| Env var | Where to get it | Required | |---|---|---| | META_APP_ID | Meta for Developers → My Apps → your app → Settings → Basic | Yes | | META_APP_SECRET | Same page | Yes | | META_ACCESS_TOKEN | Graph API Explorer for testing; System User token for production | Yes | | META_AD_ACCOUNT_ID | Meta Business Manager → Ad Accounts; format: act_XXXXXXXXXX | Yes |

Store credentials in a .env file (gitignored by the scaffold) or your preferred secrets manager. Never commit credentials to source control.

Sandbox / test mode

Google Ads — test accounts (recommended for development): Create a test manager account at ads.google.com/home/tools/manager-accounts/ and use the same developer token as your production account. Test accounts cannot interact with production accounts and do not serve ads or accumulate spend. Note: test accounts return empty serving metrics (impressions, cost, conversions are zero); the verifier agent is aware of this and will not flag zeroed metrics as discrepancies when GOOGLE_ADS_TEST_MODE=true is set.

GOOGLE_ADS_TEST_MODE=true  # set to acknowledge zero-metric test-account behaviour

Meta Marketing API — Sandbox Ad Accounts: In Meta for Developers → your app → Marketing API → Tools → Sandbox Mode, create a Sandbox Ad Account. Set META_AD_ACCOUNT_ID to the sandbox account ID (act_XXXXXXXXXX). Sandbox accounts accept all read/write API calls but never deliver ads or accumulate spend. As of 2026, sandbox accounts do not return synthetic insights data — plan to use a real (low-budget) account for end-to-end insights testing.

Mutation gate: By default, ADS_MUTATIONS_ENABLED is unset, which means the MCP policy blocks all write tools at the harness boundary. To enable mutations after completing your sandbox testing:

ADS_MUTATIONS_ENABLED=true  # enables budget updates, campaign pause/enable

Even with mutations enabled, all mutation calls go through validate_only first. The verifier agent confirms the mutation landed before reporting success.


Usage

Slash command

/ads-analyze [--days N] [--level campaign|adset|ad] [--platform google|meta|both]
  • --days — lookback window in days (default 7)
  • --level — granularity (default campaign)
  • --platform — which platform(s) to query (default both)

Representative prompts

/ads-analyze --days 30 --level campaign

The planner will fan out fetchers for each platform, collect campaign-level metrics for the last 30 days, run the verifier spot-check, then surface a cross-channel spend summary.

Show me which campaigns are under-pacing their daily budget as of today.

The planner interprets this as an analysis task, dispatches the fetcher with --days 1 --level campaign, and applies budget-pacing logic before the verifier confirms the figures.

What was our blended CPC across Google and Meta last week?

Cross-channel metrics query; the planner normalises Google's cost_micros / clicks with Meta's cpc field before presenting a unified answer.

With mutations enabled (ADS_MUTATIONS_ENABLED=true):

Pause all Google Ads campaigns with a 7-day CPA above $50.

The planner identifies candidates via a read query, runs validate_only mutations on each, presents the dry-run results for confirmation, then — only after the operator confirms — executes the live mutations. The verifier re-reads each campaign's status to confirm PAUSED landed.


Safety

| Control | Default | Override | |---|---|---| | Mutations blocked by MCP policy | Blocked (default-deny) | ADS_MUTATIONS_ENABLED=true | | Google Ads validate_only pre-check | Always on for any mutation path | Not bypassable from prompts | | Google Ads test account | Recommended for dev | Set GOOGLE_ADS_CUSTOMER_ID to test account ID | | Meta Sandbox Ad Account | Recommended for dev | Set META_AD_ACCOUNT_ID to sandbox act_ ID | | Verification gate (re-read before "done") | Always on | Not bypassable from prompts | | Audit log | Always on (.harness/audit.jsonl) | Not bypassable from prompts | | Secrets in env only | Enforced by scaffold | Never written to scaffolded files |

The audit log records every API call (read or attempted mutation) with timestamp, tool name, arguments hash, and response status. It is append-only and not cleared by the harness.


How it works

Agent pipeline

Operator prompt
       │
       ▼
 ads-planner (Tier 3 — frontier model)
   Interprets intent; decides accounts, date range, metrics, level.
   Dispatches ads-fetcher in parallel fan-out.
       │
       ▼
 ads-fetcher × N (Tier 2 — cheap/fast model)
   One claim per (platform × account).
   Google: customer.report() with GAQL.
   Meta:   AdAccount('act_...').getInsights([...fields], {date_preset}).
   Returns raw JSON rows.
       │
       ▼
 Tier 1 WASM booster (no LLM)
   Normalises cost_micros → spend_usd, unifies field names,
   deduplicates pagination cursors.
       │
       ▼
 ads-verifier (Tier 2 — cheap/fast model)
   Re-reads a random 10% sample via independent API calls.
   Compares figures; flags if delta > 1%.
   If GOOGLE_ADS_TEST_MODE=true, accepts zero metrics as valid.
   On mutation paths: re-reads the mutated resource to confirm change landed.
       │
       ▼
 ads-planner (Tier 3 — frontier model)
   Synthesises cross-channel summary.
   Applies budget-pacing logic.
   Surfaces final answer only after verifier clears.

MCP policy — granted tools

The .harness/mcp-policy.json grants exactly these tools by default:

  • ads.google.report — GAQL report query (read)
  • ads.google.query — direct GAQL query (read)
  • ads.meta.getInsights — Insights API (read)
  • ads.meta.getCampaigns — campaign list (read)
  • ads.meta.getAdSets — ad-set list (read)
  • ads.google.validate_mutate — dry-run validation only (never live)
  • audit.log — all calls logged

Mutation tools (ads.google.mutate, ads.meta.createCampaign, ads.meta.updateCampaign) are listed in mutation_grants and are hard-blocked unless ADS_MUTATIONS_ENABLED=true is present in the environment. Denied calls are hard errors with audit-log entries, not silent drops.

Routing tiers

| Tier | What runs there | Model class | |---|---|---| | Tier 1 (WASM, no LLM) | cost_microsspend_usd normalisation; field unification | None | | Tier 2 (cheap) | ads-fetcher fan-out; ads-verifier spot-checks; pagination cursor walking | Haiku-class | | Tier 3 (frontier) | ads-planner synthesis; anomaly narrative; budget-pacing analysis | Sonnet/Opus-class |


Links


License

MIT. Built on metaharness.