@revclamp/google-ads-transparency-mcp-actor 1.0.1 → 1.0.2
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 +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/apify_actor.mjs +9 -0
- package/src/mcp_server.mjs +1 -1
- package/src/rpc_client.mjs +36 -7
package/actor_config.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"manifest": {
|
|
23
23
|
"name": "google-ads-transparency-mcp-actor",
|
|
24
|
-
"version": "1.0.
|
|
24
|
+
"version": "1.0.2",
|
|
25
25
|
"title": "RevClamp AdPulse - For Google Ads Transparency Center (MCP & Apify Actor)",
|
|
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-actor",
|
|
3
3
|
"mcpName": "com.revclamp/google-ads-transparency-mcp-actor",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "RevClamp AdPulse - For Google Ads Transparency Center (MCP & Apify Actor). Independent intelligence tool by RevClamp; not affiliated with or endorsed by 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-actor",
|
|
4
4
|
"title": "RevClamp AdPulse (Google Ads Transparency MCP)",
|
|
5
5
|
"description": "Google Ads Transparency Center advertiser & creative intelligence MCP server by RevClamp.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.2",
|
|
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-actor",
|
|
30
|
-
"version": "1.0.
|
|
30
|
+
"version": "1.0.2",
|
|
31
31
|
"transport": {
|
|
32
32
|
"type": "stdio"
|
|
33
33
|
}
|
package/src/apify_actor.mjs
CHANGED
|
@@ -1255,6 +1255,7 @@ export async function runGoogleAdsTransparencyActor(input = {}, hooks = {}) {
|
|
|
1255
1255
|
advertiserId: targetItem.advertiserId,
|
|
1256
1256
|
creativeId: targetItem.creativeId,
|
|
1257
1257
|
includeAdCopy: Boolean(input?.includeAdCopy),
|
|
1258
|
+
fallbackPreviewUrl: targetItem.previewUrl,
|
|
1258
1259
|
fetchImpl: hooks.fetchImpl,
|
|
1259
1260
|
nowFn: nowMs,
|
|
1260
1261
|
sleepFn,
|
|
@@ -1282,6 +1283,14 @@ export async function runGoogleAdsTransparencyActor(input = {}, hooks = {}) {
|
|
|
1282
1283
|
rateLimitedInEnrich = true;
|
|
1283
1284
|
}
|
|
1284
1285
|
}
|
|
1286
|
+
} else if (detailRes.ok && detailRes.untrustedAdContent) {
|
|
1287
|
+
targetItem.untrustedAdContent = detailRes.untrustedAdContent;
|
|
1288
|
+
if (
|
|
1289
|
+
detailRes.untrustedAdContent.extractionStatus === 'UPSTREAM_RATE_LIMITED' ||
|
|
1290
|
+
detailRes.untrustedAdContent.contentJsFetchStatus === 'UPSTREAM_RATE_LIMITED'
|
|
1291
|
+
) {
|
|
1292
|
+
rateLimitedInEnrich = true;
|
|
1293
|
+
}
|
|
1285
1294
|
}
|
|
1286
1295
|
}
|
|
1287
1296
|
}
|
package/src/mcp_server.mjs
CHANGED
|
@@ -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-actor',
|
|
45
45
|
title: 'RevClamp AdPulse - For Google Ads Transparency Center (MCP & Apify Actor)',
|
|
46
|
-
version: '1.0.
|
|
46
|
+
version: '1.0.2',
|
|
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)
|
package/src/rpc_client.mjs
CHANGED
|
@@ -1928,7 +1928,7 @@ export async function resolveAdvertiserEntities(options = {}) {
|
|
|
1928
1928
|
const endpointUrl = options.endpointUrl || GOOGLE_ADS_RPC_ENDPOINTS.SearchSuggestions;
|
|
1929
1929
|
const rpcRes = await executeGoogleAdsRpcPost(
|
|
1930
1930
|
endpointUrl,
|
|
1931
|
-
{ '1': searchQueryStr, '2':
|
|
1931
|
+
{ '1': searchQueryStr, '2': 50, '3': 10 },
|
|
1932
1932
|
options
|
|
1933
1933
|
);
|
|
1934
1934
|
|
|
@@ -2291,8 +2291,20 @@ function isAllowedLandingHostname(hostname) {
|
|
|
2291
2291
|
return true;
|
|
2292
2292
|
}
|
|
2293
2293
|
|
|
2294
|
+
const VAST_CDATA_WRAPPER_SUFFIX_RE =
|
|
2295
|
+
/(?:\]\]>|\]\]%3E|%5D%5D%3E|%5D%5D%253E|<\/[A-Za-z0-9_:-]+>|%3C\/[A-Za-z0-9_:-]+%3E|%3C%2F[A-Za-z0-9_:-]+%3E|%253C%2F[A-Za-z0-9_:-]+%253E).*$/i;
|
|
2296
|
+
|
|
2297
|
+
function stripVastXmlCdataArtifacts(rawStr) {
|
|
2298
|
+
if (typeof rawStr !== 'string' || !rawStr) return '';
|
|
2299
|
+
return rawStr
|
|
2300
|
+
.replace(/^(?:<!\[CDATA\[|%3C!%5BCDATA%5B)+/i, '')
|
|
2301
|
+
.replace(VAST_CDATA_WRAPPER_SUFFIX_RE, '')
|
|
2302
|
+
.replace(/[\]>]+$/, '')
|
|
2303
|
+
.trim();
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2294
2306
|
function normalizeCandidateLandingUrl(rawUrl) {
|
|
2295
|
-
const cleaned = sanitizeUntrustedAdText(rawUrl, 2048);
|
|
2307
|
+
const cleaned = stripVastXmlCdataArtifacts(sanitizeUntrustedAdText(rawUrl, 2048));
|
|
2296
2308
|
if (!cleaned) return null;
|
|
2297
2309
|
try {
|
|
2298
2310
|
const parsed = new URL(cleaned);
|
|
@@ -2363,7 +2375,7 @@ function unwrapCandidateUrlOrAdUrlParam(rawUrl) {
|
|
|
2363
2375
|
const adUrlMatch = decodedRaw.match(/[?&]adurl=([^"'\s&\\]+)/i);
|
|
2364
2376
|
if (adUrlMatch && adUrlMatch[1]) {
|
|
2365
2377
|
try {
|
|
2366
|
-
const target = decodeURIComponent(adUrlMatch[1]);
|
|
2378
|
+
const target = decodeURIComponent(stripVastXmlCdataArtifacts(adUrlMatch[1]));
|
|
2367
2379
|
const normalizedTarget = normalizeCandidateLandingUrl(target);
|
|
2368
2380
|
if (normalizedTarget) return normalizedTarget;
|
|
2369
2381
|
} catch {
|
|
@@ -2583,7 +2595,9 @@ export function extractStaticAdCopyFromContentJs(rawJsText) {
|
|
|
2583
2595
|
const adUrlParamMatch = surface.match(/[?&]adurl=(https?%3A%2F%2F[^"'\s&\\]+)/i);
|
|
2584
2596
|
if (adUrlParamMatch && adUrlParamMatch[1]) {
|
|
2585
2597
|
try {
|
|
2586
|
-
landingPageUrl = normalizeCandidateLandingUrl(
|
|
2598
|
+
landingPageUrl = normalizeCandidateLandingUrl(
|
|
2599
|
+
decodeURIComponent(stripVastXmlCdataArtifacts(adUrlParamMatch[1]))
|
|
2600
|
+
);
|
|
2587
2601
|
if (landingPageUrl) break;
|
|
2588
2602
|
} catch {
|
|
2589
2603
|
landingPageUrl = null;
|
|
@@ -3558,17 +3572,33 @@ export async function getCreativeDetailsById(options = {}) {
|
|
|
3558
3572
|
};
|
|
3559
3573
|
}
|
|
3560
3574
|
|
|
3575
|
+
const nowFn = typeof options.nowFn === 'function' ? options.nowFn : Date.now;
|
|
3576
|
+
const fallbackPreviewUrl =
|
|
3577
|
+
typeof options.fallbackPreviewUrl === 'string' && options.fallbackPreviewUrl.trim()
|
|
3578
|
+
? options.fallbackPreviewUrl.trim()
|
|
3579
|
+
: null;
|
|
3580
|
+
|
|
3561
3581
|
if (canary.status === 'EMPTY_RESULT') {
|
|
3582
|
+
let fallbackUntrustedAdContent = null;
|
|
3583
|
+
if (
|
|
3584
|
+
options.includeAdCopy === true &&
|
|
3585
|
+
fallbackPreviewUrl &&
|
|
3586
|
+
(!options.deadlineAtMs || nowFn() + 200 < options.deadlineAtMs)
|
|
3587
|
+
) {
|
|
3588
|
+
fallbackUntrustedAdContent = await fetchAndExtractStaticAdCopy(fallbackPreviewUrl, options);
|
|
3589
|
+
}
|
|
3562
3590
|
return {
|
|
3563
3591
|
ok: true,
|
|
3564
3592
|
status: 'EMPTY_RESULT',
|
|
3565
3593
|
detail: null,
|
|
3594
|
+
...(fallbackUntrustedAdContent ? { untrustedAdContent: fallbackUntrustedAdContent } : {}),
|
|
3566
3595
|
rpcAttempts: rpcRes.attempts,
|
|
3567
3596
|
};
|
|
3568
3597
|
}
|
|
3569
3598
|
|
|
3570
3599
|
const detailObj = rpcRes.data['1'];
|
|
3571
3600
|
const { previewUrl, archiveImageUrls } = extractCreativeAssetUrls(detailObj?.['5'] ?? detailObj);
|
|
3601
|
+
const effectivePreviewUrl = previewUrl || fallbackPreviewUrl;
|
|
3572
3602
|
const rawRegions = Array.isArray(detailObj?.['17']) ? detailObj['17'] : [];
|
|
3573
3603
|
const regionalPresence = rawRegions
|
|
3574
3604
|
.filter((r) => r && typeof r === 'object')
|
|
@@ -3588,15 +3618,14 @@ export async function getCreativeDetailsById(options = {}) {
|
|
|
3588
3618
|
let untrustedAdContent = extractStaticAdCopyFromContentJs(
|
|
3589
3619
|
JSON.stringify(detailObj?.['5'] ?? detailObj)
|
|
3590
3620
|
);
|
|
3591
|
-
const nowFn = typeof options.nowFn === 'function' ? options.nowFn : Date.now;
|
|
3592
3621
|
if (
|
|
3593
3622
|
!untrustedAdContent.headline &&
|
|
3594
3623
|
!untrustedAdContent.bodyText &&
|
|
3595
3624
|
options.includeAdCopy === true &&
|
|
3596
|
-
|
|
3625
|
+
effectivePreviewUrl &&
|
|
3597
3626
|
(!options.deadlineAtMs || nowFn() + 200 < options.deadlineAtMs)
|
|
3598
3627
|
) {
|
|
3599
|
-
const fetchedCopy = await fetchAndExtractStaticAdCopy(
|
|
3628
|
+
const fetchedCopy = await fetchAndExtractStaticAdCopy(effectivePreviewUrl, options);
|
|
3600
3629
|
if (fetchedCopy.extractionStatus === 'EXTRACTED') {
|
|
3601
3630
|
untrustedAdContent = {
|
|
3602
3631
|
...fetchedCopy,
|