@revclamp/google-ads-transparency-mcp 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -122,6 +122,7 @@ npm test
122
122
  | **[RevClamp Enterprise](https://revclamp.com/mcp)** | [`revclamp-mcp`](https://www.npmjs.com/package/revclamp-mcp) | Pre-deploy subscription revenue, webhook idempotency & compliance guard (`Stripe`, `Paddle`, `Lemon Squeezy`, `Shopify`) |
123
123
  | **[RevClamp AdPulse](https://apify.com/revclamp/google-ads-transparency-mcp)** | [`@revclamp/google-ads-transparency-mcp`](https://www.npmjs.com/package/@revclamp/google-ads-transparency-mcp) | Verified Google Ads advertiser entity disambiguation & active/historical creative intelligence |
124
124
  | **[RevClamp TalentPulse](https://apify.com/revclamp/unified-ats-jobs-mcp)** | [`@revclamp/unified-ats-jobs-mcp`](https://www.npmjs.com/package/@revclamp/unified-ats-jobs-mcp) | Unified 5-in-1 B2B ATS job postings, salary ranges & tech-stack hiring signals (`Greenhouse`, `Workday CXS`, `Ashby`, `Lever`, `SmartRecruiters`) |
125
+ | **[RevClamp ContactPulse](https://apify.com/revclamp/b2b-domain-contact-enricher-mcp)** | [`@revclamp/b2b-domain-contact-enricher-mcp`](https://www.npmjs.com/package/@revclamp/b2b-domain-contact-enricher-mcp) | B2B domain role-contact enrichment, RFC 9116 `security.txt` audit & DNS-over-HTTPS MX/SPF/DMARC deliverability posture |
125
126
 
126
127
  ## Terms of Use, Data Provenance & Independent Tool Disclaimer
127
128
 
package/actor_config.json CHANGED
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "manifest": {
23
23
  "name": "google-ads-transparency-mcp",
24
- "version": "1.0.8",
24
+ "version": "1.0.9",
25
25
  "title": "RevClamp AdPulse - Google Ads Transparency Scraper & MCP",
26
26
  "websiteUrl": "https://revclamp.com",
27
27
  "description": "Find verified Google Ads advertiser entities and extract active or historical ad creatives (Text, Image, Video), preview links, and run dates without browser or proxy overhead. Independent intelligence tool by RevClamp; not affiliated with or endorsed by Google LLC."
package/index.mjs CHANGED
@@ -28,6 +28,7 @@ import {
28
28
  COMPOSITE_CURSOR_PREFIX,
29
29
  MAX_CURSOR_LENGTH,
30
30
  MAX_QUERY_LENGTH,
31
+ KNOWN_ADVERTISER_DIRECTORY,
31
32
  CORPORATE_LEGAL_SUFFIX_TOKENS,
32
33
  collapseDottedAcronyms,
33
34
  foldBrandText,
@@ -100,6 +101,7 @@ export {
100
101
  COMPOSITE_CURSOR_PREFIX,
101
102
  MAX_CURSOR_LENGTH,
102
103
  MAX_QUERY_LENGTH,
104
+ KNOWN_ADVERTISER_DIRECTORY,
103
105
  CORPORATE_LEGAL_SUFFIX_TOKENS,
104
106
  collapseDottedAcronyms,
105
107
  foldBrandText,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@revclamp/google-ads-transparency-mcp",
3
3
  "mcpName": "com.revclamp/google-ads-transparency-mcp",
4
- "version": "1.0.8",
4
+ "version": "1.0.9",
5
5
  "type": "module",
6
6
  "description": "RevClamp AdPulse - Google Ads Transparency Scraper & MCP. Find verified advertiser entities and extract active or historical ad creatives (Text, Image, Video), preview links, and run dates with zero browser overhead. Independent tool by RevClamp; not affiliated with Google LLC.",
7
7
  "homepage": "https://revclamp.com",
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "com.revclamp/google-ads-transparency-mcp",
4
4
  "title": "RevClamp AdPulse - Google Ads Transparency Scraper & MCP",
5
5
  "description": "Google Ads Transparency Center advertiser & creative intelligence MCP server by RevClamp.",
6
- "version": "1.0.8",
6
+ "version": "1.0.9",
7
7
  "websiteUrl": "https://revclamp.com",
8
8
  "repository": {
9
9
  "url": "https://github.com/lhphong09t2/revclamp-mcp",
@@ -27,7 +27,7 @@
27
27
  "registryType": "npm",
28
28
  "registryBaseUrl": "https://registry.npmjs.org",
29
29
  "identifier": "@revclamp/google-ads-transparency-mcp",
30
- "version": "1.0.8",
30
+ "version": "1.0.9",
31
31
  "runtimeHint": "npx",
32
32
  "transport": {
33
33
  "type": "stdio"
@@ -43,7 +43,7 @@ export const ENCODED_BRAND_ICON_DATA_URI = `data:image/svg+xml;utf8,${encodeURIC
43
43
  export const MCP_SERVER_INFO = Object.freeze({
44
44
  name: 'google-ads-transparency-mcp',
45
45
  title: 'RevClamp AdPulse - Google Ads Transparency Scraper & MCP',
46
- version: '1.0.8',
46
+ version: '1.0.9',
47
47
  description:
48
48
  'Find verified Google Ads advertiser entities and extract active or historical ad creatives (Text, Image, Video), preview links, and run dates. Independent intelligence tool by RevClamp; not affiliated with or endorsed by Google LLC.',
49
49
  // Forward-compatible MCP Registry & Glama metadata extensions (ignored by older stdio clients)
@@ -56,6 +56,182 @@ export const MAX_QUERY_LENGTH = 512;
56
56
  const MIN_VALID_EPOCH_SEC = 946684800; // 2000-01-01T00:00:00Z
57
57
  const MAX_VALID_EPOCH_SEC = 4102444800; // 2100-01-01T00:00:00Z
58
58
 
59
+ /**
60
+ * Curated directory of verified Google Ads Transparency legal entities (`AR...`)
61
+ * for top global brands so common queries resolve in 0 SearchSuggestions RPC calls
62
+ * and degrade gracefully if upstream SearchSuggestions encounters rate-limiting.
63
+ */
64
+ export const KNOWN_ADVERTISER_DIRECTORY = Object.freeze({
65
+ nike: Object.freeze({
66
+ advertiserId: 'AR16735076323512287233',
67
+ advertiserName: 'Nike, Inc.',
68
+ regionCode: 'US',
69
+ minAds: 9000,
70
+ maxAds: 10000,
71
+ }),
72
+ stripe: Object.freeze({
73
+ advertiserId: 'AR16951989901585285121',
74
+ advertiserName: 'Stripe, Inc.',
75
+ regionCode: 'US',
76
+ minAds: 8000,
77
+ maxAds: 9000,
78
+ }),
79
+ shopify: Object.freeze({
80
+ advertiserId: 'AR01625195283841286145',
81
+ advertiserName: 'Shopify Inc.',
82
+ regionCode: 'CA',
83
+ minAds: 40000,
84
+ maxAds: 50000,
85
+ }),
86
+ apple: Object.freeze({
87
+ advertiserId: 'AR03186575737440174081',
88
+ advertiserName: 'Apple Inc.',
89
+ regionCode: 'US',
90
+ minAds: 1000,
91
+ maxAds: 2000,
92
+ }),
93
+ openai: Object.freeze({
94
+ advertiserId: 'AR02788840342533701633',
95
+ advertiserName: 'OpenAI OpCo, LLC',
96
+ regionCode: 'US',
97
+ minAds: 40000,
98
+ maxAds: 50000,
99
+ }),
100
+ hubspot: Object.freeze({
101
+ advertiserId: 'AR10072600183532683265',
102
+ advertiserName: 'Hubspot, Inc.',
103
+ regionCode: 'US',
104
+ minAds: 2000,
105
+ maxAds: 3000,
106
+ }),
107
+ google: Object.freeze({
108
+ advertiserId: 'AR00753945370957971457',
109
+ advertiserName: 'Google LLC',
110
+ regionCode: 'US',
111
+ minAds: 21,
112
+ maxAds: 21,
113
+ }),
114
+ microsoft: Object.freeze({
115
+ advertiserId: 'AR15495076796520464385',
116
+ advertiserName: 'Microsoft Corporation',
117
+ regionCode: 'US',
118
+ minAds: 60000,
119
+ maxAds: 70000,
120
+ }),
121
+ amazon: Object.freeze({
122
+ advertiserId: 'AR12815412684405080065',
123
+ advertiserName: 'Amazon',
124
+ regionCode: 'US',
125
+ minAds: 2000,
126
+ maxAds: 3000,
127
+ }),
128
+ meta: Object.freeze({
129
+ advertiserId: 'AR17753757646030110721',
130
+ advertiserName: 'Meta',
131
+ regionCode: 'US',
132
+ minAds: 500,
133
+ maxAds: 600,
134
+ }),
135
+ netflix: Object.freeze({
136
+ advertiserId: 'AR11279752625278418945',
137
+ advertiserName: 'Netflix, Inc.',
138
+ regionCode: 'US',
139
+ minAds: 9000,
140
+ maxAds: 10000,
141
+ }),
142
+ airbnb: Object.freeze({
143
+ advertiserId: 'AR15908226811872411649',
144
+ advertiserName: 'Airbnb, Inc.',
145
+ regionCode: 'US',
146
+ minAds: 1000000,
147
+ maxAds: 1100000,
148
+ }),
149
+ uber: Object.freeze({
150
+ advertiserId: 'AR10766558596670947329',
151
+ advertiserName: 'Uber B.V.',
152
+ regionCode: 'NL',
153
+ minAds: 20000,
154
+ maxAds: 30000,
155
+ }),
156
+ salesforce: Object.freeze({
157
+ advertiserId: 'AR14047752120044617729',
158
+ advertiserName: 'Salesforce, Inc.',
159
+ regionCode: 'US',
160
+ minAds: 20000,
161
+ maxAds: 30000,
162
+ }),
163
+ adobe: Object.freeze({
164
+ advertiserId: 'AR15000638237259595777',
165
+ advertiserName: 'Adobe, Inc',
166
+ regionCode: 'US',
167
+ minAds: 100000,
168
+ maxAds: 200000,
169
+ }),
170
+ cloudflare: Object.freeze({
171
+ advertiserId: 'AR10379917531786772481',
172
+ advertiserName: 'Cloudflare, Inc.',
173
+ regionCode: 'US',
174
+ minAds: 500,
175
+ maxAds: 600,
176
+ }),
177
+ datadog: Object.freeze({
178
+ advertiserId: 'AR02089091059349979137',
179
+ advertiserName: 'DATADOG, INC.',
180
+ regionCode: 'US',
181
+ minAds: 9000,
182
+ maxAds: 10000,
183
+ }),
184
+ datadoghq: Object.freeze({
185
+ advertiserId: 'AR02089091059349979137',
186
+ advertiserName: 'DATADOG, INC.',
187
+ regionCode: 'US',
188
+ minAds: 9000,
189
+ maxAds: 10000,
190
+ }),
191
+ vercel: Object.freeze({
192
+ advertiserId: 'AR03920035274037395457',
193
+ advertiserName: 'Vercel Inc.',
194
+ regionCode: 'US',
195
+ minAds: 200,
196
+ maxAds: 300,
197
+ }),
198
+ figma: Object.freeze({
199
+ advertiserId: 'AR15466204033491927041',
200
+ advertiserName: 'Figma INC',
201
+ regionCode: 'US',
202
+ minAds: 1000,
203
+ maxAds: 2000,
204
+ }),
205
+ atlassian: Object.freeze({
206
+ advertiserId: 'AR16220030717323640833',
207
+ advertiserName: 'Atlassian US, Inc.',
208
+ regionCode: 'US',
209
+ minAds: 20000,
210
+ maxAds: 30000,
211
+ }),
212
+ snowflake: Object.freeze({
213
+ advertiserId: 'AR00247099845179867137',
214
+ advertiserName: 'Snowflake Inc.',
215
+ regionCode: 'US',
216
+ minAds: 4000,
217
+ maxAds: 5000,
218
+ }),
219
+ twilio: Object.freeze({
220
+ advertiserId: 'AR18227580619968217089',
221
+ advertiserName: 'Twilio Inc.',
222
+ regionCode: 'US',
223
+ minAds: 1000,
224
+ maxAds: 2000,
225
+ }),
226
+ booking: Object.freeze({
227
+ advertiserId: 'AR02934798844673654785',
228
+ advertiserName: 'Booking.com',
229
+ regionCode: 'NL',
230
+ minAds: 16500000,
231
+ maxAds: 16600000,
232
+ }),
233
+ });
234
+
59
235
  /**
60
236
  * Canonical Google Ads Geo Target Criterion IDs mapped by ISO 3166-1 alpha-2 country code.
61
237
  * Verified against live `LookupService/GetCreativeById` field `17` (`{ "1": 2840, "5": 20261104 }`)
@@ -1529,10 +1705,15 @@ export async function executeGoogleAdsRpcPost(endpointUrl, fReqPayload, options
1529
1705
  headers: {
1530
1706
  'content-type': 'application/x-www-form-urlencoded;charset=UTF-8',
1531
1707
  accept: 'application/json, text/plain, */*',
1708
+ 'accept-language': 'en-US,en;q=0.9',
1532
1709
  origin: 'https://adstransparency.google.com',
1533
1710
  referer: 'https://adstransparency.google.com/',
1711
+ 'sec-fetch-dest': 'empty',
1712
+ 'sec-fetch-mode': 'cors',
1713
+ 'sec-fetch-site': 'same-origin',
1714
+ 'x-same-domain': '1',
1534
1715
  'user-agent':
1535
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
1716
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
1536
1717
  },
1537
1718
  body: encodedBody,
1538
1719
  redirect: 'manual',
@@ -1942,6 +2123,35 @@ export async function resolveAdvertiserEntities(options = {}) {
1942
2123
  };
1943
2124
  }
1944
2125
 
2126
+ // Match single-brand or domain stem against KNOWN_ADVERTISER_DIRECTORY when !includeSubsidiaries
2127
+ const directoryKey =
2128
+ queryMeta.brandTokens.length === 1 ? String(queryMeta.brandStem || '').toLowerCase() : '';
2129
+ const rawKnownEntry = directoryKey ? KNOWN_ADVERTISER_DIRECTORY[directoryKey] || null : null;
2130
+ const knownDirectoryEntity =
2131
+ rawKnownEntry && (!rawCountry || rawCountry === 'ALL' || rawCountry === rawKnownEntry.regionCode)
2132
+ ? scoreAdvertiserCandidate(rawKnownEntry, queryMeta, rawCountry)
2133
+ : null;
2134
+
2135
+ const singleEntityDirectoryEligible = maxEntities === 1;
2136
+ const shouldUseDirectoryBeforeRpc =
2137
+ Boolean(knownDirectoryEntity) &&
2138
+ singleEntityDirectoryEligible &&
2139
+ options.preferDirectory === true;
2140
+
2141
+ if (shouldUseDirectoryBeforeRpc) {
2142
+ return {
2143
+ ok: true,
2144
+ status: 'OK',
2145
+ resolutionSource: 'KNOWN_ADVERTISER_DIRECTORY',
2146
+ queryMeta,
2147
+ primaryEntity: knownDirectoryEntity,
2148
+ selectedEntities: [knownDirectoryEntity],
2149
+ allCandidates: [knownDirectoryEntity],
2150
+ rpcAttempts: 0,
2151
+ billableCount: 0,
2152
+ };
2153
+ }
2154
+
1945
2155
  // Multi-token domains (e.g. on-running.com) join all tokens ("on running") rather than truncating to first token
1946
2156
  const searchQueryStr = queryMeta.isDomainQuery
1947
2157
  ? queryMeta.brandTokens.length > 1
@@ -1957,6 +2167,24 @@ export async function resolveAdvertiserEntities(options = {}) {
1957
2167
  );
1958
2168
 
1959
2169
  if (!rpcRes.ok) {
2170
+ if (
2171
+ rpcRes.status === 'UPSTREAM_RATE_LIMITED' &&
2172
+ knownDirectoryEntity &&
2173
+ singleEntityDirectoryEligible &&
2174
+ options.fallbackToDirectoryOnRateLimit !== false
2175
+ ) {
2176
+ return {
2177
+ ok: true,
2178
+ status: 'OK',
2179
+ resolutionSource: 'KNOWN_ADVERTISER_DIRECTORY_FALLBACK',
2180
+ queryMeta,
2181
+ primaryEntity: knownDirectoryEntity,
2182
+ selectedEntities: [knownDirectoryEntity],
2183
+ allCandidates: [knownDirectoryEntity],
2184
+ rpcAttempts: rpcRes.attempts,
2185
+ billableCount: 0,
2186
+ };
2187
+ }
1960
2188
  return {
1961
2189
  ok: false,
1962
2190
  status: rpcRes.status,
@@ -2760,7 +2988,13 @@ export async function fetchAndExtractStaticAdCopy(previewUrl, options = {}) {
2760
2988
  method: 'GET',
2761
2989
  headers: {
2762
2990
  accept: 'application/javascript, text/javascript, text/plain, */*',
2991
+ 'accept-language': 'en-US,en;q=0.9',
2763
2992
  referer: 'https://adstransparency.google.com/',
2993
+ 'sec-fetch-dest': 'script',
2994
+ 'sec-fetch-mode': 'no-cors',
2995
+ 'sec-fetch-site': 'cross-site',
2996
+ 'user-agent':
2997
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
2764
2998
  },
2765
2999
  redirect: 'manual',
2766
3000
  signal: controller.signal,