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

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/actor_config.json CHANGED
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "manifest": {
23
23
  "name": "google-ads-transparency-mcp",
24
- "version": "1.0.9",
24
+ "version": "1.0.10",
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/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.9",
4
+ "version": "1.0.10",
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.9",
6
+ "version": "1.0.10",
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.9",
30
+ "version": "1.0.10",
31
31
  "runtimeHint": "npx",
32
32
  "transport": {
33
33
  "type": "stdio"
@@ -61,7 +61,8 @@ const MAX_VALID_EPOCH_SEC = 4102444800; // 2100-01-01T00:00:00Z
61
61
  * for top global brands so common queries resolve in 0 SearchSuggestions RPC calls
62
62
  * and degrade gracefully if upstream SearchSuggestions encounters rate-limiting.
63
63
  */
64
- export const KNOWN_ADVERTISER_DIRECTORY = Object.freeze({
64
+ export const KNOWN_ADVERTISER_DIRECTORY = Object.freeze(
65
+ Object.assign(Object.create(null), {
65
66
  nike: Object.freeze({
66
67
  advertiserId: 'AR16735076323512287233',
67
68
  advertiserName: 'Nike, Inc.',
@@ -230,7 +231,85 @@ export const KNOWN_ADVERTISER_DIRECTORY = Object.freeze({
230
231
  minAds: 16500000,
231
232
  maxAds: 16600000,
232
233
  }),
233
- });
234
+ oracle: Object.freeze({
235
+ advertiserId: 'AR08687048956523315201',
236
+ advertiserName: 'Oracle America, Inc.',
237
+ regionCode: 'US',
238
+ minAds: 3000,
239
+ maxAds: 4000,
240
+ }),
241
+ sap: Object.freeze({
242
+ advertiserId: 'AR14859734842907557889',
243
+ advertiserName: 'SAP SE',
244
+ regionCode: 'DE',
245
+ minAds: 2000,
246
+ maxAds: 3000,
247
+ }),
248
+ cisco: Object.freeze({
249
+ advertiserId: 'AR13942224429984514049',
250
+ advertiserName: 'Cisco Systems, Inc.',
251
+ regionCode: 'US',
252
+ minAds: 7000,
253
+ maxAds: 8000,
254
+ }),
255
+ nvidia: Object.freeze({
256
+ advertiserId: 'AR01839840586606051329',
257
+ advertiserName: 'NVIDIA Corporation',
258
+ regionCode: 'US',
259
+ minAds: 600,
260
+ maxAds: 700,
261
+ }),
262
+ canva: Object.freeze({
263
+ advertiserId: 'AR06374349942554624001',
264
+ advertiserName: 'Canva, Pty Ltd',
265
+ regionCode: 'AU',
266
+ minAds: 30000,
267
+ maxAds: 40000,
268
+ }),
269
+ asana: Object.freeze({
270
+ advertiserId: 'AR06554691289986629633',
271
+ advertiserName: 'Asana, Inc.',
272
+ regionCode: 'US',
273
+ minAds: 6000,
274
+ maxAds: 7000,
275
+ }),
276
+ zendesk: Object.freeze({
277
+ advertiserId: 'AR13756467712907804673',
278
+ advertiserName: 'Zendesk, Inc.',
279
+ regionCode: 'US',
280
+ minAds: 3000,
281
+ maxAds: 4000,
282
+ }),
283
+ okta: Object.freeze({
284
+ advertiserId: 'AR10734849505959608321',
285
+ advertiserName: 'Okta, Inc',
286
+ regionCode: 'US',
287
+ minAds: 1000,
288
+ maxAds: 2000,
289
+ }),
290
+ crowdstrike: Object.freeze({
291
+ advertiserId: 'AR13642683652595777537',
292
+ advertiserName: 'CROWDSTRIKE, INC.',
293
+ regionCode: 'US',
294
+ minAds: 8000,
295
+ maxAds: 9000,
296
+ }),
297
+ servicenow: Object.freeze({
298
+ advertiserId: 'AR04119835984416210945',
299
+ advertiserName: 'ServiceNow, Inc',
300
+ regionCode: 'US',
301
+ minAds: 10000,
302
+ maxAds: 20000,
303
+ }),
304
+ workday: Object.freeze({
305
+ advertiserId: 'AR09563861960156512257',
306
+ advertiserName: 'Workday, Inc.',
307
+ regionCode: 'US',
308
+ minAds: 5000,
309
+ maxAds: 6000,
310
+ }),
311
+ })
312
+ );
234
313
 
235
314
  /**
236
315
  * Canonical Google Ads Geo Target Criterion IDs mapped by ISO 3166-1 alpha-2 country code.
@@ -605,6 +684,13 @@ export const CORPORATE_LEGAL_SUFFIX_TOKENS = new Set([
605
684
  'lp',
606
685
  'holdings',
607
686
  'holding',
687
+ 'opco',
688
+ 'holdco',
689
+ 'topco',
690
+ 'bidco',
691
+ 'midco',
692
+ 'newco',
693
+ 'finco',
608
694
  'group',
609
695
  'international',
610
696
  'global',
@@ -827,10 +913,18 @@ export function parseBrandQueryTokens(rawQuery) {
827
913
  : rawBrandTokens;
828
914
 
829
915
  const brandStem = brandTokens[0] || foldedStemSource.replace(/[^\p{L}\p{N}]/gu, '');
830
- const brandStemWithoutHq =
831
- isDomainQuery && brandStem.length >= 6 && brandStem.endsWith('hq')
916
+ const candidateStemWithoutHq =
917
+ brandStem.length >= 6 && brandStem.endsWith('hq')
832
918
  ? brandStem.slice(0, -2)
833
919
  : brandStem;
920
+ const brandStemWithoutHq =
921
+ candidateStemWithoutHq !== brandStem &&
922
+ brandTokens.length === 1 &&
923
+ (isDomainQuery ||
924
+ Object.hasOwn(KNOWN_ADVERTISER_DIRECTORY, brandStem) ||
925
+ Object.hasOwn(KNOWN_ADVERTISER_DIRECTORY, candidateStemWithoutHq))
926
+ ? candidateStemWithoutHq
927
+ : brandStem;
834
928
 
835
929
  return {
836
930
  rawQuery: trimmed,
@@ -1449,8 +1543,18 @@ export function scoreAdvertiserCandidate(candidate, queryMeta, preferredCountry
1449
1543
  ? Boolean(leadingEntityWords[0] === brandTokens[0] && allBrandTokensPresentAsWords)
1450
1544
  : firstWordMatchesStem;
1451
1545
 
1546
+ const hasHqStemReduction = Boolean(
1547
+ brandTokens.length === 1 &&
1548
+ brandStemWithoutHq &&
1549
+ brandStemWithoutHq !== brandStem &&
1550
+ brandStemWithoutHq.length >= 2
1551
+ );
1452
1552
  let domainConcatPrefixWords = [];
1453
- if (queryMeta?.isDomainQuery && brandStemWithoutHq && brandStemWithoutHq.length >= 2) {
1553
+ if (
1554
+ (queryMeta?.isDomainQuery || hasHqStemReduction) &&
1555
+ brandStemWithoutHq &&
1556
+ brandStemWithoutHq.length >= 2
1557
+ ) {
1454
1558
  for (const wordList of [leadingEntityWords, nameWords]) {
1455
1559
  let acc = '';
1456
1560
  for (let k = 0; k < Math.min(5, wordList.length); k += 1) {
@@ -1470,12 +1574,11 @@ export function scoreAdvertiserCandidate(candidate, queryMeta, preferredCountry
1470
1574
  }
1471
1575
 
1472
1576
  const domainStemPrefixMatch = Boolean(
1473
- queryMeta?.isDomainQuery &&
1577
+ (queryMeta?.isDomainQuery || hasHqStemReduction) &&
1474
1578
  brandStemWithoutHq &&
1475
1579
  brandStemWithoutHq.length >= 2 &&
1476
- ((leadingEntityWords[0] === brandStemWithoutHq &&
1477
- (brandTokens.length <= 1 || allBrandTokensPresentAsWords)) ||
1478
- domainConcatPrefixWords.length > 0)
1580
+ (brandTokens.length <= 1 || allBrandTokensPresentAsWords) &&
1581
+ (leadingEntityWords[0] === brandStemWithoutHq || domainConcatPrefixWords.length > 0)
1479
1582
  );
1480
1583
 
1481
1584
  const matchedCoreTokens = exactBrandWordMatch
@@ -2126,10 +2229,26 @@ export async function resolveAdvertiserEntities(options = {}) {
2126
2229
  // Match single-brand or domain stem against KNOWN_ADVERTISER_DIRECTORY when !includeSubsidiaries
2127
2230
  const directoryKey =
2128
2231
  queryMeta.brandTokens.length === 1 ? String(queryMeta.brandStem || '').toLowerCase() : '';
2129
- const rawKnownEntry = directoryKey ? KNOWN_ADVERTISER_DIRECTORY[directoryKey] || null : null;
2232
+ const fallbackDirectoryKey =
2233
+ queryMeta.brandTokens.length === 1
2234
+ ? String(queryMeta.brandStemWithoutHq || '').toLowerCase()
2235
+ : '';
2236
+ const rawKnownEntry = directoryKey
2237
+ ? (Object.hasOwn(KNOWN_ADVERTISER_DIRECTORY, directoryKey)
2238
+ ? KNOWN_ADVERTISER_DIRECTORY[directoryKey]
2239
+ : null) ||
2240
+ (fallbackDirectoryKey && Object.hasOwn(KNOWN_ADVERTISER_DIRECTORY, fallbackDirectoryKey)
2241
+ ? KNOWN_ADVERTISER_DIRECTORY[fallbackDirectoryKey]
2242
+ : null) ||
2243
+ null
2244
+ : null;
2245
+ const normalizedFilterCountry = rawCountry === 'UK' ? 'GB' : rawCountry;
2130
2246
  const knownDirectoryEntity =
2131
- rawKnownEntry && (!rawCountry || rawCountry === 'ALL' || rawCountry === rawKnownEntry.regionCode)
2132
- ? scoreAdvertiserCandidate(rawKnownEntry, queryMeta, rawCountry)
2247
+ rawKnownEntry &&
2248
+ (!normalizedFilterCountry ||
2249
+ normalizedFilterCountry === 'ALL' ||
2250
+ normalizedFilterCountry === rawKnownEntry.regionCode)
2251
+ ? scoreAdvertiserCandidate(rawKnownEntry, queryMeta, normalizedFilterCountry)
2133
2252
  : null;
2134
2253
 
2135
2254
  const singleEntityDirectoryEligible = maxEntities === 1;
@@ -2152,12 +2271,21 @@ export async function resolveAdvertiserEntities(options = {}) {
2152
2271
  };
2153
2272
  }
2154
2273
 
2155
- // Multi-token domains (e.g. on-running.com) join all tokens ("on running") rather than truncating to first token
2274
+ // Multi-token domains (e.g. on-running.com) join all tokens ("on running") rather than truncating to first token;
2275
+ // single-token -hq domains/queries (e.g. datadoghq.com, datadoghq) search by brandStemWithoutHq ("datadog").
2276
+ const effectiveSingleTokenStem =
2277
+ queryMeta.brandStemWithoutHq && queryMeta.brandStemWithoutHq !== queryMeta.brandStem
2278
+ ? queryMeta.brandStemWithoutHq
2279
+ : queryMeta.brandStem;
2156
2280
  const searchQueryStr = queryMeta.isDomainQuery
2157
2281
  ? queryMeta.brandTokens.length > 1
2158
2282
  ? queryMeta.brandTokens.join(' ')
2159
- : queryMeta.brandStem
2160
- : queryMeta.rawQuery;
2283
+ : effectiveSingleTokenStem
2284
+ : queryMeta.brandTokens.length === 1 &&
2285
+ queryMeta.brandStemWithoutHq &&
2286
+ queryMeta.brandStemWithoutHq !== queryMeta.brandStem
2287
+ ? queryMeta.brandStemWithoutHq
2288
+ : queryMeta.rawQuery;
2161
2289
 
2162
2290
  const endpointUrl = options.endpointUrl || GOOGLE_ADS_RPC_ENDPOINTS.SearchSuggestions;
2163
2291
  const rpcRes = await executeGoogleAdsRpcPost(