mcp-scraper 0.3.27 → 0.3.40
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/dist/bin/api-server.cjs +2475 -1154
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +346 -7
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +7 -4
- package/dist/bin/mcp-stdio-server.js.map +1 -1
- package/dist/bin/paa-harvest.cjs +109 -5
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +2 -2
- package/dist/{chunk-TRNSXR46.js → chunk-DVRPXPYH.js} +22 -3
- package/dist/chunk-DVRPXPYH.js.map +1 -0
- package/dist/{chunk-QSSH4RCX.js → chunk-GMTS35L6.js} +111 -7
- package/dist/chunk-GMTS35L6.js.map +1 -0
- package/dist/chunk-I5EFBTYE.js +7 -0
- package/dist/chunk-I5EFBTYE.js.map +1 -0
- package/dist/{chunk-H74L743B.js → chunk-KJWNVQFL.js} +39 -2
- package/dist/chunk-KJWNVQFL.js.map +1 -0
- package/dist/{chunk-6KHGYJIT.js → chunk-QLQYNE7E.js} +255 -14
- package/dist/chunk-QLQYNE7E.js.map +1 -0
- package/dist/{db-FP2ABUU4.js → db-LYQENFPW.js} +2 -2
- package/dist/index.cjs +109 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2 -2
- package/dist/{server-KUNOK47B.js → server-HB7WJO3N.js} +1155 -274
- package/dist/server-HB7WJO3N.js.map +1 -0
- package/dist/{worker-NV4GTIRG.js → worker-TL4J65DE.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-6KHGYJIT.js.map +0 -1
- package/dist/chunk-DYV72F6A.js +0 -7
- package/dist/chunk-DYV72F6A.js.map +0 -1
- package/dist/chunk-H74L743B.js.map +0 -1
- package/dist/chunk-QSSH4RCX.js.map +0 -1
- package/dist/chunk-TRNSXR46.js.map +0 -1
- package/dist/server-KUNOK47B.js.map +0 -1
- /package/dist/{db-FP2ABUU4.js.map → db-LYQENFPW.js.map} +0 -0
- /package/dist/{worker-NV4GTIRG.js.map → worker-TL4J65DE.js.map} +0 -0
|
@@ -101,7 +101,7 @@ function renderInstallTerminal(options) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// src/version.ts
|
|
104
|
-
var PACKAGE_VERSION = "0.3.
|
|
104
|
+
var PACKAGE_VERSION = "0.3.40";
|
|
105
105
|
|
|
106
106
|
// bin/mcp-scraper-install.ts
|
|
107
107
|
var noColor = process.argv.includes("--no-color") || process.env.NO_COLOR !== void 0 || process.env.FORCE_COLOR === "0" || !process.stdout.isTTY;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/install-terminal.ts","../../src/version.ts","../../bin/mcp-scraper-install.ts"],"sourcesContent":["export interface InstallTerminalOptions {\n color?: boolean\n version: string\n apiKeyConfigured?: boolean\n}\n\ntype Tone = 'cyan' | 'lime' | 'amber' | 'red' | 'muted' | 'bold'\n\nconst CODES: Record<Tone | 'reset', string> = {\n reset: '\\x1b[0m',\n cyan: '\\x1b[36m',\n lime: '\\x1b[32m',\n amber: '\\x1b[33m',\n red: '\\x1b[31m',\n muted: '\\x1b[90m',\n bold: '\\x1b[1m',\n}\n\nfunction colorize(value: string, tone: Tone, enabled: boolean): string {\n if (!enabled) return value\n return `${CODES[tone]}${value}${CODES.reset}`\n}\n\nfunction toolRow(label: string, tools: string[], enabled: boolean): string {\n const padded = label.padEnd(9, ' ')\n return ` ${colorize(padded, 'muted', enabled)} ${tools.join(colorize(' . ', 'muted', enabled))}`\n}\n\nexport function renderInstallTerminal(options: InstallTerminalOptions): string {\n const color = options.color ?? true\n const apiKeyValue = options.apiKeyConfigured ? '$MCP_SCRAPER_API_KEY' : 'sk_live_your_key'\n const ascii = String.raw`\n __ __ ____ ____ \n| \\/ |/ ___| _ \\\n| |\\/| | | | |_) |\n| | | | |___| __/\n|_| |_|\\____|_|\n\n ____ ____ ____ _ ____ _____ ____\n/ ___| / ___| _ \\ / \\ | _ \\| ____| _ \\\n\\___ \\| | | |_) | / _ \\ | |_) | _| | |_) |\n ___) | |___| _ < / ___ \\| __/| |___| _ <\n|____/ \\____|_| \\_\\/_/ \\_\\_| |_____|_| \\_\\\n`\n\n const claudeCommand = [\n `MCP_SCRAPER_API_KEY=${apiKeyValue} npx -y -p mcp-scraper@latest \\\\`,\n ' mcp-scraper-cli agent install claude --apply',\n ].join('\\n')\n\n const codexConfig = [\n '[mcp_servers.mcp-scraper]',\n 'command = \"npx\"',\n 'args = [\"-y\", \"-p\", \"mcp-scraper@latest\", \"mcp-scraper\"]',\n `env = { MCP_SCRAPER_API_KEY = \"${apiKeyValue}\" }`,\n ].join('\\n')\n\n return [\n colorize(`mcp-scraper v${options.version}`, 'bold', color),\n colorize('> mcp-scraper-install', 'muted', color),\n colorize(ascii, 'amber', color),\n `${colorize('MCP Scraper Agent', 'cyan', color)} . v${options.version} . mcpscraper.dev`,\n '1/1 install surfaces ready',\n colorize('Newest: saved browser profiles that hold MANY site logins. Share the watch_url, user signs in, then verify with browser_profile_list. Browser sessions are direct/no-proxy by default.', 'lime', color),\n '',\n `${colorize('Tools', 'cyan', color)} ${colorize('(48 MCP tools)', 'muted', color)}`,\n toolRow('search', ['harvest_paa', 'search_serp', 'maps_search', 'maps_place_intel'], color),\n toolRow('extract', ['extract_url', 'map_site_urls', 'extract_site', 'audit_site', 'directory_workflow'], color),\n toolRow('build', ['rank_tracker_workflow', 'cron plan', 'database prompt'], color),\n toolRow('media', ['youtube_harvest', 'youtube_transcribe', 'facebook_ad_search', 'facebook_page_intel', 'facebook_ad_transcribe', 'facebook_video_transcribe', 'instagram_profile_content', 'instagram_media_download', 'reddit_thread'], color),\n toolRow('browser', ['browser_open', 'browser_profile_connect', 'browser_profile_list', 'browser_close', 'browser_screenshot', 'browser_read', 'browser_locate', 'browser_replay_mark', 'browser_replay_annotate'], color),\n toolRow('account', ['credits_info', 'reports', 'MCP resources'], color),\n `${colorize('Workflows', 'cyan', color)} ${colorize('(MCP + CLI + API)', 'muted', color)}`,\n toolRow('route', ['workflow_list', 'workflow_suggest', 'workflow_run', 'workflow_step', 'workflow_status', 'workflow_artifact_read'], color),\n toolRow('seo', ['directory', 'agent-packet', 'competitive audit', 'map/serp comparison', 'PAA/AIO briefs', 'scheduled runs'], color),\n '',\n colorize('Usage tips:', 'amber', color),\n 'Run mcp-scraper-install for this visible card. Run mcp-scraper-cli for setup utilities and subcommands.',\n 'Run mcp-scraper in a human terminal to print this card; MCP clients get the same command as a silent stdio server.',\n 'Explicit card command: npx -y -p mcp-scraper@latest mcp-scraper-install',\n 'Customer auth setup: run browser_profile_connect, send the watch_url, let the user sign in, then call browser_profile_list until AUTHENTICATED.',\n 'Stack logins / reconnect: run browser_profile_connect again with the same profile name and another domain to add accounts or refresh a login.',\n 'Start with workflow_suggest for broad jobs like market analysis, ICP research, CRO audits, brand briefs, content gaps, and AI visibility.',\n 'For MCP clients, use mcp-scraper so one install can mix SERP, Maps, browser, reports, and saved MCP resources.',\n 'If you hit the concurrency limit, add an extra slot for $5/month with mcp-scraper-cli billing concurrency checkout.',\n '',\n `${colorize('Ready.', 'lime', color)} Install the combined MCP server with one command:`,\n '',\n colorize('Setup doctor', 'amber', color),\n 'npx -y -p mcp-scraper@latest mcp-scraper-cli doctor',\n '',\n colorize('Hosted profile setup', 'amber', color),\n 'In your MCP client, call browser_profile_connect with email=\"seo@example.com\" and domain=\"chatgpt.com\".',\n 'Give the returned watch_url to the user. After they sign in, call browser_profile_list, then browser_open with the returned profile. Add more logins by calling browser_profile_connect again with the same profile and a new domain.',\n '',\n colorize('Claude Code one-command setup', 'amber', color),\n claudeCommand,\n 'Then fully exit Claude Code and open a new Claude terminal. Check with: claude mcp list',\n '',\n colorize('Codex config', 'amber', color),\n codexConfig,\n '',\n colorize('Claude Desktop Extension', 'amber', color),\n 'Download: https://mcpscraper.dev/downloads/mcp-scraper.mcpb',\n '',\n colorize('Safety note:', 'muted', color),\n 'mcp-scraper prints this card only when stdin/stdout are an interactive TTY. In MCP clients it writes only JSON-RPC to stdout.',\n 'Use --stdio or MCP_SCRAPER_FORCE_STDIO=1 to force server mode from a terminal.',\n '',\n ].join('\\n')\n}\n","export const PACKAGE_VERSION = '0.3.
|
|
1
|
+
{"version":3,"sources":["../../src/install-terminal.ts","../../src/version.ts","../../bin/mcp-scraper-install.ts"],"sourcesContent":["export interface InstallTerminalOptions {\n color?: boolean\n version: string\n apiKeyConfigured?: boolean\n}\n\ntype Tone = 'cyan' | 'lime' | 'amber' | 'red' | 'muted' | 'bold'\n\nconst CODES: Record<Tone | 'reset', string> = {\n reset: '\\x1b[0m',\n cyan: '\\x1b[36m',\n lime: '\\x1b[32m',\n amber: '\\x1b[33m',\n red: '\\x1b[31m',\n muted: '\\x1b[90m',\n bold: '\\x1b[1m',\n}\n\nfunction colorize(value: string, tone: Tone, enabled: boolean): string {\n if (!enabled) return value\n return `${CODES[tone]}${value}${CODES.reset}`\n}\n\nfunction toolRow(label: string, tools: string[], enabled: boolean): string {\n const padded = label.padEnd(9, ' ')\n return ` ${colorize(padded, 'muted', enabled)} ${tools.join(colorize(' . ', 'muted', enabled))}`\n}\n\nexport function renderInstallTerminal(options: InstallTerminalOptions): string {\n const color = options.color ?? true\n const apiKeyValue = options.apiKeyConfigured ? '$MCP_SCRAPER_API_KEY' : 'sk_live_your_key'\n const ascii = String.raw`\n __ __ ____ ____ \n| \\/ |/ ___| _ \\\n| |\\/| | | | |_) |\n| | | | |___| __/\n|_| |_|\\____|_|\n\n ____ ____ ____ _ ____ _____ ____\n/ ___| / ___| _ \\ / \\ | _ \\| ____| _ \\\n\\___ \\| | | |_) | / _ \\ | |_) | _| | |_) |\n ___) | |___| _ < / ___ \\| __/| |___| _ <\n|____/ \\____|_| \\_\\/_/ \\_\\_| |_____|_| \\_\\\n`\n\n const claudeCommand = [\n `MCP_SCRAPER_API_KEY=${apiKeyValue} npx -y -p mcp-scraper@latest \\\\`,\n ' mcp-scraper-cli agent install claude --apply',\n ].join('\\n')\n\n const codexConfig = [\n '[mcp_servers.mcp-scraper]',\n 'command = \"npx\"',\n 'args = [\"-y\", \"-p\", \"mcp-scraper@latest\", \"mcp-scraper\"]',\n `env = { MCP_SCRAPER_API_KEY = \"${apiKeyValue}\" }`,\n ].join('\\n')\n\n return [\n colorize(`mcp-scraper v${options.version}`, 'bold', color),\n colorize('> mcp-scraper-install', 'muted', color),\n colorize(ascii, 'amber', color),\n `${colorize('MCP Scraper Agent', 'cyan', color)} . v${options.version} . mcpscraper.dev`,\n '1/1 install surfaces ready',\n colorize('Newest: saved browser profiles that hold MANY site logins. Share the watch_url, user signs in, then verify with browser_profile_list. Browser sessions are direct/no-proxy by default.', 'lime', color),\n '',\n `${colorize('Tools', 'cyan', color)} ${colorize('(48 MCP tools)', 'muted', color)}`,\n toolRow('search', ['harvest_paa', 'search_serp', 'maps_search', 'maps_place_intel'], color),\n toolRow('extract', ['extract_url', 'map_site_urls', 'extract_site', 'audit_site', 'directory_workflow'], color),\n toolRow('build', ['rank_tracker_workflow', 'cron plan', 'database prompt'], color),\n toolRow('media', ['youtube_harvest', 'youtube_transcribe', 'facebook_ad_search', 'facebook_page_intel', 'facebook_ad_transcribe', 'facebook_video_transcribe', 'instagram_profile_content', 'instagram_media_download', 'reddit_thread'], color),\n toolRow('browser', ['browser_open', 'browser_profile_connect', 'browser_profile_list', 'browser_close', 'browser_screenshot', 'browser_read', 'browser_locate', 'browser_replay_mark', 'browser_replay_annotate'], color),\n toolRow('account', ['credits_info', 'reports', 'MCP resources'], color),\n `${colorize('Workflows', 'cyan', color)} ${colorize('(MCP + CLI + API)', 'muted', color)}`,\n toolRow('route', ['workflow_list', 'workflow_suggest', 'workflow_run', 'workflow_step', 'workflow_status', 'workflow_artifact_read'], color),\n toolRow('seo', ['directory', 'agent-packet', 'competitive audit', 'map/serp comparison', 'PAA/AIO briefs', 'scheduled runs'], color),\n '',\n colorize('Usage tips:', 'amber', color),\n 'Run mcp-scraper-install for this visible card. Run mcp-scraper-cli for setup utilities and subcommands.',\n 'Run mcp-scraper in a human terminal to print this card; MCP clients get the same command as a silent stdio server.',\n 'Explicit card command: npx -y -p mcp-scraper@latest mcp-scraper-install',\n 'Customer auth setup: run browser_profile_connect, send the watch_url, let the user sign in, then call browser_profile_list until AUTHENTICATED.',\n 'Stack logins / reconnect: run browser_profile_connect again with the same profile name and another domain to add accounts or refresh a login.',\n 'Start with workflow_suggest for broad jobs like market analysis, ICP research, CRO audits, brand briefs, content gaps, and AI visibility.',\n 'For MCP clients, use mcp-scraper so one install can mix SERP, Maps, browser, reports, and saved MCP resources.',\n 'If you hit the concurrency limit, add an extra slot for $5/month with mcp-scraper-cli billing concurrency checkout.',\n '',\n `${colorize('Ready.', 'lime', color)} Install the combined MCP server with one command:`,\n '',\n colorize('Setup doctor', 'amber', color),\n 'npx -y -p mcp-scraper@latest mcp-scraper-cli doctor',\n '',\n colorize('Hosted profile setup', 'amber', color),\n 'In your MCP client, call browser_profile_connect with email=\"seo@example.com\" and domain=\"chatgpt.com\".',\n 'Give the returned watch_url to the user. After they sign in, call browser_profile_list, then browser_open with the returned profile. Add more logins by calling browser_profile_connect again with the same profile and a new domain.',\n '',\n colorize('Claude Code one-command setup', 'amber', color),\n claudeCommand,\n 'Then fully exit Claude Code and open a new Claude terminal. Check with: claude mcp list',\n '',\n colorize('Codex config', 'amber', color),\n codexConfig,\n '',\n colorize('Claude Desktop Extension', 'amber', color),\n 'Download: https://mcpscraper.dev/downloads/mcp-scraper.mcpb',\n '',\n colorize('Safety note:', 'muted', color),\n 'mcp-scraper prints this card only when stdin/stdout are an interactive TTY. In MCP clients it writes only JSON-RPC to stdout.',\n 'Use --stdio or MCP_SCRAPER_FORCE_STDIO=1 to force server mode from a terminal.',\n '',\n ].join('\\n')\n}\n","export const PACKAGE_VERSION = '0.3.40'\n","#!/usr/bin/env node\nimport { renderInstallTerminal } from '../src/install-terminal.js'\nimport { PACKAGE_VERSION } from '../src/version.js'\n\nconst noColor =\n process.argv.includes('--no-color') ||\n process.env.NO_COLOR !== undefined ||\n process.env.FORCE_COLOR === '0' ||\n !process.stdout.isTTY\n\nconst help = process.argv.includes('--help') || process.argv.includes('-h')\nif (help) {\n process.stdout.write([\n 'Usage: mcp-scraper-install [--no-color]',\n '',\n 'Prints the branded MCP Scraper terminal install card and copyable install commands.',\n 'mcp-scraper prints the same card in a human terminal and runs as the MCP stdio server in clients.',\n '',\n ].join('\\n'))\n process.exit(0)\n}\n\nprocess.stdout.write(renderInstallTerminal({\n version: PACKAGE_VERSION,\n color: !noColor,\n apiKeyConfigured: Boolean(process.env.MCP_SCRAPER_API_KEY?.trim()),\n}))\n"],"mappings":"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","names":[]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "../chunk-T3VZD2I4.js";
|
|
5
5
|
import {
|
|
6
6
|
PACKAGE_VERSION
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-I5EFBTYE.js";
|
|
8
8
|
|
|
9
9
|
// bin/mcp-scraper-install.ts
|
|
10
10
|
var noColor = process.argv.includes("--no-color") || process.env.NO_COLOR !== void 0 || process.env.FORCE_COLOR === "0" || !process.stdout.isTTY;
|
|
@@ -270,6 +270,15 @@ var HttpMcpToolExecutor = class {
|
|
|
270
270
|
facebookVideoTranscribe(input) {
|
|
271
271
|
return this.call("/facebook/video-transcribe", input, this.httpTimeoutOverrideMs ?? 24e4);
|
|
272
272
|
}
|
|
273
|
+
googleAdsSearch(input) {
|
|
274
|
+
return this.call("/google-ads/search", input, this.httpTimeoutOverrideMs ?? 24e4);
|
|
275
|
+
}
|
|
276
|
+
googleAdsPageIntel(input) {
|
|
277
|
+
return this.call("/google-ads/page-intel", input, this.httpTimeoutOverrideMs ?? 3e5);
|
|
278
|
+
}
|
|
279
|
+
googleAdsTranscribe(input) {
|
|
280
|
+
return this.call("/google-ads/transcribe", input, this.httpTimeoutOverrideMs ?? 24e4);
|
|
281
|
+
}
|
|
273
282
|
instagramProfileContent(input) {
|
|
274
283
|
return this.call("/instagram/profile-content", withDefaultHostedProfile(input), this.httpTimeoutOverrideMs ?? 24e4);
|
|
275
284
|
}
|
|
@@ -459,7 +468,7 @@ render();
|
|
|
459
468
|
}
|
|
460
469
|
|
|
461
470
|
// src/version.ts
|
|
462
|
-
var PACKAGE_VERSION = "0.3.
|
|
471
|
+
var PACKAGE_VERSION = "0.3.40";
|
|
463
472
|
|
|
464
473
|
// src/mcp/browser-agent-tool-schemas.ts
|
|
465
474
|
var import_zod = require("zod");
|
|
@@ -2700,7 +2709,9 @@ ${paaRows}` : "## People Also Ask\n*Google did not return a People Also Ask bloc
|
|
|
2700
2709
|
${serpRows}` : "";
|
|
2701
2710
|
const aiSection = aiOvw?.detected && aiOvw.text ? `
|
|
2702
2711
|
## AI Overview
|
|
2703
|
-
> ${truncate(aiOvw.text, 600)}`
|
|
2712
|
+
> ${truncate(aiOvw.text, 600)}` + (aiOvw.shareUrl ? `
|
|
2713
|
+
|
|
2714
|
+
**Shareable link:** ${aiOvw.shareUrl}` : "") : "";
|
|
2704
2715
|
const statsLine = durationMs ? `
|
|
2705
2716
|
## Stats
|
|
2706
2717
|
- Status: ${diagnostics?.completionStatus ?? (flat.length ? "paa_found" : "no_paa")} \xB7 Questions: ${flat.length} \xB7 Duration: ${(durationMs / 1e3).toFixed(1)}s` : "";
|
|
@@ -2733,7 +2744,7 @@ ${paaTable}${serpTable}${entityIdsSection(entityIds)}${aiSection}${statsLine}${d
|
|
|
2733
2744
|
domain: String(r.domain ?? ""),
|
|
2734
2745
|
snippet: r.snippet ?? null
|
|
2735
2746
|
})),
|
|
2736
|
-
aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null } : null,
|
|
2747
|
+
aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null, shareUrl: aiOvw.shareUrl ?? null } : null,
|
|
2737
2748
|
entityIds: entityIds ? { entities: entityIds.entities ?? [], kgIds: entityIds.kgIds ?? [], cids: entityIds.cids ?? [], gcids: entityIds.gcids ?? [] } : null,
|
|
2738
2749
|
durationMs: durationMs ?? null
|
|
2739
2750
|
}
|
|
@@ -2765,7 +2776,9 @@ ${serpRows}` : "## Organic Results\n*None found*";
|
|
|
2765
2776
|
${localRows}` : "";
|
|
2766
2777
|
const aiSection = aiOvw?.detected && aiOvw.text ? `
|
|
2767
2778
|
## AI Overview
|
|
2768
|
-
> ${truncate(aiOvw.text, 600)}`
|
|
2779
|
+
> ${truncate(aiOvw.text, 600)}` + (aiOvw.shareUrl ? `
|
|
2780
|
+
|
|
2781
|
+
**Shareable link:** ${aiOvw.shareUrl}` : "") : "";
|
|
2769
2782
|
const tips = `
|
|
2770
2783
|
---
|
|
2771
2784
|
\u{1F4A1} **Tips**
|
|
@@ -2794,7 +2807,7 @@ ${serpTable}${localSection}${entityIdsSection(entityIds)}${aiSection}${debugSect
|
|
|
2794
2807
|
reviewCount: b.reviewCount ?? null,
|
|
2795
2808
|
websiteUrl: b.websiteUrl ?? null
|
|
2796
2809
|
})),
|
|
2797
|
-
aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null } : null,
|
|
2810
|
+
aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null, shareUrl: aiOvw.shareUrl ?? null } : null,
|
|
2798
2811
|
entityIds: entityIds ? { entities: entityIds.entities ?? [], kgIds: entityIds.kgIds ?? [], cids: entityIds.cids ?? [], gcids: entityIds.gcids ?? [] } : null
|
|
2799
2812
|
}
|
|
2800
2813
|
};
|
|
@@ -3352,6 +3365,136 @@ ${rows}`,
|
|
|
3352
3365
|
}
|
|
3353
3366
|
};
|
|
3354
3367
|
}
|
|
3368
|
+
function formatGoogleAdsSearch(raw, input) {
|
|
3369
|
+
const parsed = parseData(raw);
|
|
3370
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
3371
|
+
const d = parsed.data;
|
|
3372
|
+
const advertisers = d.advertisers ?? [];
|
|
3373
|
+
const rows = advertisers.map(
|
|
3374
|
+
(a, i) => `| ${i + 1} | ${cell(a.name)} | ${cell(a.domain)} | ${a.approxAdCount ?? "\u2014"} | \`${a.advertiserId ?? "\u2014"}\` |`
|
|
3375
|
+
).join("\n");
|
|
3376
|
+
const full = [
|
|
3377
|
+
`# Google Ads Transparency Search: "${input.query}"`,
|
|
3378
|
+
`**${advertisers.length} advertisers found**${d.region ? ` \xB7 ${d.region}` : ""}`,
|
|
3379
|
+
`
|
|
3380
|
+
## Advertisers
|
|
3381
|
+
| # | Name | Domain | ~Ads | Advertiser ID |
|
|
3382
|
+
|---|------|--------|------|---------------|
|
|
3383
|
+
${rows}`,
|
|
3384
|
+
`
|
|
3385
|
+
---
|
|
3386
|
+
\u{1F4A1} **Tips**
|
|
3387
|
+
- Pull an advertiser's ads: use \`google_ads_page_intel\` with \`advertiserId\`
|
|
3388
|
+
- Or pass a \`domain\` directly to \`google_ads_page_intel\``
|
|
3389
|
+
].join("\n");
|
|
3390
|
+
return {
|
|
3391
|
+
...oneBlock(full),
|
|
3392
|
+
structuredContent: {
|
|
3393
|
+
query: input.query,
|
|
3394
|
+
region: d.region ?? "US",
|
|
3395
|
+
advertiserCount: advertisers.length,
|
|
3396
|
+
advertisers: advertisers.map((a) => ({
|
|
3397
|
+
advertiserId: a.advertiserId ?? null,
|
|
3398
|
+
name: a.name ?? null,
|
|
3399
|
+
domain: a.domain ?? null,
|
|
3400
|
+
approxAdCount: typeof a.approxAdCount === "number" ? a.approxAdCount : null,
|
|
3401
|
+
detailUrl: a.detailUrl ?? null
|
|
3402
|
+
}))
|
|
3403
|
+
}
|
|
3404
|
+
};
|
|
3405
|
+
}
|
|
3406
|
+
function formatGoogleAdsPageIntel(raw, input) {
|
|
3407
|
+
const parsed = parseData(raw);
|
|
3408
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
3409
|
+
const d = parsed.data;
|
|
3410
|
+
const ads = d.ads ?? [];
|
|
3411
|
+
const rows = ads.map((a, i) => {
|
|
3412
|
+
const media = a.youtubeVideoId ? `yt:${a.youtubeVideoId}` : a.videoUrl ? "video" : a.imageUrls.length ? `${a.imageUrls.length} img` : "\u2014";
|
|
3413
|
+
return `| ${i + 1} | ${cell(a.format)} | ${media} | \`${a.creativeId ?? "\u2014"}\` |`;
|
|
3414
|
+
}).join("\n");
|
|
3415
|
+
const label = d.advertiserName ?? input.advertiserId ?? input.domain ?? "";
|
|
3416
|
+
const full = [
|
|
3417
|
+
`# Google Ads Transparency: ${label}`,
|
|
3418
|
+
`**${ads.length} creatives** \xB7 ${d.videoCount ?? 0} video \xB7 ${d.imageCount ?? 0} image \xB7 ${d.textCount ?? 0} text${d.region ? ` \xB7 ${d.region}` : ""}`,
|
|
3419
|
+
`
|
|
3420
|
+
## Creatives
|
|
3421
|
+
| # | Format | Media | Creative ID |
|
|
3422
|
+
|---|--------|-------|-------------|
|
|
3423
|
+
${rows}`,
|
|
3424
|
+
`
|
|
3425
|
+
---
|
|
3426
|
+
\u{1F4A1} **Tips**
|
|
3427
|
+
- Transcribe a video ad: pass its \`videoUrl\` to \`google_ads_transcribe\`, or its \`youtubeVideoId\` to \`youtube_transcribe\`
|
|
3428
|
+
- Full ad detail is at each creative's \`detailUrl\``
|
|
3429
|
+
].join("\n");
|
|
3430
|
+
return {
|
|
3431
|
+
...oneBlock(full),
|
|
3432
|
+
structuredContent: {
|
|
3433
|
+
advertiserId: d.advertiserId ?? null,
|
|
3434
|
+
advertiserName: d.advertiserName ?? null,
|
|
3435
|
+
domain: d.domain ?? null,
|
|
3436
|
+
region: d.region ?? "US",
|
|
3437
|
+
totalCreatives: typeof d.totalCreatives === "number" ? d.totalCreatives : ads.length,
|
|
3438
|
+
videoCount: d.videoCount ?? 0,
|
|
3439
|
+
imageCount: d.imageCount ?? 0,
|
|
3440
|
+
textCount: d.textCount ?? 0,
|
|
3441
|
+
ads: ads.map((a) => ({
|
|
3442
|
+
creativeId: a.creativeId ?? null,
|
|
3443
|
+
advertiserId: a.advertiserId ?? null,
|
|
3444
|
+
format: a.format ?? null,
|
|
3445
|
+
lastShown: a.lastShown ?? null,
|
|
3446
|
+
detailUrl: a.detailUrl ?? null,
|
|
3447
|
+
landingDomain: a.landingDomain ?? null,
|
|
3448
|
+
imageUrls: Array.isArray(a.imageUrls) ? a.imageUrls : [],
|
|
3449
|
+
youtubeVideoId: a.youtubeVideoId ?? null,
|
|
3450
|
+
videoUrl: a.videoUrl ?? null,
|
|
3451
|
+
variations: typeof a.variations === "number" ? a.variations : null
|
|
3452
|
+
}))
|
|
3453
|
+
}
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3456
|
+
function formatGoogleAdsTranscribe(raw, input) {
|
|
3457
|
+
const parsed = parseData(raw);
|
|
3458
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
3459
|
+
const d = parsed.data;
|
|
3460
|
+
const text = d.text ?? "";
|
|
3461
|
+
const chunks = d.chunks ?? [];
|
|
3462
|
+
const durSec = d.durationMs ? (d.durationMs / 1e3).toFixed(0) : "\u2014";
|
|
3463
|
+
const words = wordCount(text);
|
|
3464
|
+
const chunkRows = chunks.slice(0, 50).map((c) => {
|
|
3465
|
+
const sec = Number.isFinite(c.timestamp[0]) ? Math.floor(c.timestamp[0]) : 0;
|
|
3466
|
+
const mm = String(Math.floor(sec / 60)).padStart(2, "0");
|
|
3467
|
+
const ss = String(sec % 60).padStart(2, "0");
|
|
3468
|
+
return `| ${mm}:${ss} | ${cell(truncate(c.text, 120))} |`;
|
|
3469
|
+
}).join("\n");
|
|
3470
|
+
const full = [
|
|
3471
|
+
`# Google Ad Transcript`,
|
|
3472
|
+
`**Duration:** ${durSec}s \xB7 **${words} words**`,
|
|
3473
|
+
`
|
|
3474
|
+
## Full Transcript
|
|
3475
|
+
${text}`,
|
|
3476
|
+
chunks.length ? `
|
|
3477
|
+
## Timestamped Chunks
|
|
3478
|
+
| Time | Text |
|
|
3479
|
+
|------|------|
|
|
3480
|
+
${chunkRows}` : "",
|
|
3481
|
+
`
|
|
3482
|
+
---
|
|
3483
|
+
\u{1F4A1} Get more ads from this advertiser: use \`google_ads_page_intel\`.`
|
|
3484
|
+
].filter(Boolean).join("\n");
|
|
3485
|
+
return {
|
|
3486
|
+
...oneBlock(full),
|
|
3487
|
+
structuredContent: {
|
|
3488
|
+
videoUrl: input.videoUrl,
|
|
3489
|
+
wordCount: words,
|
|
3490
|
+
chunkCount: chunks.length,
|
|
3491
|
+
durationMs: typeof d.durationMs === "number" ? d.durationMs : null,
|
|
3492
|
+
transcriptText: text,
|
|
3493
|
+
chunks: structuredTranscriptChunks(chunks),
|
|
3494
|
+
resolvedInputs: { videoUrl: input.videoUrl }
|
|
3495
|
+
}
|
|
3496
|
+
};
|
|
3497
|
+
}
|
|
3355
3498
|
function normalizeMapsAttempts(value) {
|
|
3356
3499
|
const attempts = Array.isArray(value) ? value : [];
|
|
3357
3500
|
return attempts.map((attempt, index) => ({
|
|
@@ -4242,6 +4385,96 @@ ${limitations.map((l) => `- ${l}`).join("\n")}` : "",
|
|
|
4242
4385
|
}
|
|
4243
4386
|
};
|
|
4244
4387
|
}
|
|
4388
|
+
function formatCaptureSerpSnapshot(raw, input) {
|
|
4389
|
+
const parsed = parseData(raw);
|
|
4390
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
4391
|
+
const d = parsed.data;
|
|
4392
|
+
const providerPayload = d;
|
|
4393
|
+
const organic = Array.isArray(d.organicResults) ? d.organicResults : Array.isArray(d.organic) ? d.organic : [];
|
|
4394
|
+
const localPack = Array.isArray(d.localPack) ? d.localPack : [];
|
|
4395
|
+
const artifacts = Array.isArray(d.artifacts) ? d.artifacts : [];
|
|
4396
|
+
const status = String(d.status ?? d.captureStatus ?? "captured");
|
|
4397
|
+
const query = typeof d.query === "string" ? d.query : typeof input.query === "string" ? input.query : null;
|
|
4398
|
+
const location = typeof d.location === "string" ? d.location : typeof input.location === "string" ? input.location : null;
|
|
4399
|
+
const capturedAt = typeof d.capturedAt === "string" ? d.capturedAt : typeof d.extractedAt === "string" ? d.extractedAt : null;
|
|
4400
|
+
const resultCount = organic.length + localPack.length;
|
|
4401
|
+
const full = [
|
|
4402
|
+
`# SERP Intelligence Snapshot: ${query ?? "query"}`,
|
|
4403
|
+
`**Status:** ${status}`,
|
|
4404
|
+
location ? `**Location:** ${location}` : "",
|
|
4405
|
+
`**Result count:** ${resultCount}`,
|
|
4406
|
+
artifacts.length ? `**Artifacts:** ${artifacts.length}` : "",
|
|
4407
|
+
"",
|
|
4408
|
+
"Use `capture_serp_page_snapshots` when you need page-level evidence for ranking URLs."
|
|
4409
|
+
].filter(Boolean).join("\n");
|
|
4410
|
+
return {
|
|
4411
|
+
...oneBlock(full),
|
|
4412
|
+
structuredContent: {
|
|
4413
|
+
schemaVersion: "serp-intelligence.capture.v1",
|
|
4414
|
+
status,
|
|
4415
|
+
query,
|
|
4416
|
+
location,
|
|
4417
|
+
capturedAt,
|
|
4418
|
+
resultCount,
|
|
4419
|
+
snapshotId: typeof d.snapshotId === "string" ? d.snapshotId : typeof d.snapshot_id === "string" ? d.snapshot_id : typeof d.id === "string" ? d.id : null,
|
|
4420
|
+
resolvedInputs: {
|
|
4421
|
+
query: input.query ?? null,
|
|
4422
|
+
location: input.location ?? null,
|
|
4423
|
+
gl: input.gl ?? null,
|
|
4424
|
+
hl: input.hl ?? null,
|
|
4425
|
+
device: input.device ?? null,
|
|
4426
|
+
proxyMode: input.proxyMode ?? null,
|
|
4427
|
+
proxyZip: input.proxyZip ?? null,
|
|
4428
|
+
pages: input.pages ?? null
|
|
4429
|
+
},
|
|
4430
|
+
artifacts,
|
|
4431
|
+
diagnostics: d.diagnostics && typeof d.diagnostics === "object" ? d.diagnostics : null,
|
|
4432
|
+
providerPayload
|
|
4433
|
+
}
|
|
4434
|
+
};
|
|
4435
|
+
}
|
|
4436
|
+
function formatCaptureSerpPageSnapshots(raw, input) {
|
|
4437
|
+
const parsed = parseData(raw);
|
|
4438
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
4439
|
+
const d = parsed.data;
|
|
4440
|
+
const captures = Array.isArray(d.captures) ? d.captures : Array.isArray(d.results) ? d.results : [];
|
|
4441
|
+
const failedCount = captures.filter((c) => c.status === "failed" || c.error).length;
|
|
4442
|
+
const status = String(d.status ?? (failedCount ? "partial" : "captured"));
|
|
4443
|
+
const rows = captures.slice(0, 25).map((capture, i) => {
|
|
4444
|
+
const url = typeof capture.url === "string" ? capture.url : "";
|
|
4445
|
+
const sourceKind = typeof capture.sourceKind === "string" ? capture.sourceKind : typeof capture.source_kind === "string" ? capture.source_kind : "configured_target";
|
|
4446
|
+
return `| ${i + 1} | ${cell(url)} | ${cell(sourceKind)} | ${cell(String(capture.status ?? (capture.error ? "failed" : "ok")))} |`;
|
|
4447
|
+
}).join("\n");
|
|
4448
|
+
const full = [
|
|
4449
|
+
"# SERP Intelligence Page Snapshots",
|
|
4450
|
+
`**Status:** ${status}`,
|
|
4451
|
+
`**Captured:** ${captures.length}`,
|
|
4452
|
+
`**Failed:** ${failedCount}`,
|
|
4453
|
+
captures.length ? `
|
|
4454
|
+
| # | URL | Source | Status |
|
|
4455
|
+
|---|-----|--------|--------|
|
|
4456
|
+
${rows}` : ""
|
|
4457
|
+
].filter(Boolean).join("\n");
|
|
4458
|
+
return {
|
|
4459
|
+
...oneBlock(full),
|
|
4460
|
+
structuredContent: {
|
|
4461
|
+
schemaVersion: "serp-intelligence.page-snapshots.v1",
|
|
4462
|
+
status,
|
|
4463
|
+
count: captures.length,
|
|
4464
|
+
failedCount,
|
|
4465
|
+
captures,
|
|
4466
|
+
resolvedInputs: {
|
|
4467
|
+
urls: input.urls ?? null,
|
|
4468
|
+
targets: input.targets ?? null,
|
|
4469
|
+
maxConcurrency: input.maxConcurrency ?? null,
|
|
4470
|
+
timeoutMs: input.timeoutMs ?? null,
|
|
4471
|
+
debug: input.debug ?? null
|
|
4472
|
+
},
|
|
4473
|
+
diagnostics: d.diagnostics && typeof d.diagnostics === "object" ? d.diagnostics : null,
|
|
4474
|
+
providerPayload: d
|
|
4475
|
+
}
|
|
4476
|
+
};
|
|
4477
|
+
}
|
|
4245
4478
|
|
|
4246
4479
|
// src/mcp/server-instructions.ts
|
|
4247
4480
|
var SERVER_INSTRUCTIONS = `
|
|
@@ -4280,6 +4513,13 @@ seam is noted so you can chain them.
|
|
|
4280
4513
|
- Transcribe an ad video -> **facebook_ad_transcribe** (takes a videoUrl from \`facebook_page_intel\`).
|
|
4281
4514
|
- Transcribe an organic reel/video/post -> **facebook_video_transcribe** (takes the Facebook url directly).
|
|
4282
4515
|
|
|
4516
|
+
## Google Ads (Ads Transparency Center)
|
|
4517
|
+
- Find advertisers by domain or brand -> **google_ads_search** (returns \`advertisers[]\` with advertiserId).
|
|
4518
|
+
- One advertiser's ads -> **google_ads_page_intel** (takes advertiserId or domain; returns \`ads[]\` with
|
|
4519
|
+
format, imageUrls, youtubeVideoId, and videoUrl).
|
|
4520
|
+
- Transcribe a video ad -> **google_ads_transcribe** (takes a googlevideo \`videoUrl\` from
|
|
4521
|
+
\`google_ads_page_intel\`), or **youtube_transcribe** for a returned \`youtubeVideoId\`.
|
|
4522
|
+
|
|
4283
4523
|
## Instagram
|
|
4284
4524
|
- Profile inventory -> **instagram_profile_content** (returns post/reel/tv urls).
|
|
4285
4525
|
- One post or reel -> **instagram_media_download** (takes a url from \`instagram_profile_content\`, or a
|
|
@@ -4489,6 +4729,20 @@ var FacebookVideoTranscribeInputSchema = {
|
|
|
4489
4729
|
url: import_zod3.z.string().url().describe("Organic Facebook reel, video, watch, post, or share URL from facebook.com, m.facebook.com, or fb.watch. The tool renders the page, extracts the best matching public Facebook CDN MP4 URL, then transcribes it. Use this when the user pastes a normal Facebook video page URL and asks for the transcript or downloadable MP4."),
|
|
4490
4730
|
quality: import_zod3.z.enum(["best", "hd", "sd"]).default("best").describe("Preferred progressive MP4 quality. Use best by default; hd prefers the highest HD progressive URL; sd forces the SD URL.")
|
|
4491
4731
|
};
|
|
4732
|
+
var GoogleAdsSearchInputSchema = {
|
|
4733
|
+
query: import_zod3.z.string().min(1).describe("A domain (e.g. getviktor.com) or advertiser/brand name to look up in the Google Ads Transparency Center. Domains resolve to the advertisers running ads that point to that site."),
|
|
4734
|
+
region: import_zod3.z.string().length(2).default("US").describe("Two-letter region code for where the ads are shown. Default US. Examples: US, CA, GB, AU."),
|
|
4735
|
+
maxResults: import_zod3.z.number().int().min(1).max(20).default(10).describe("Maximum advertisers to return. Default 10, maximum 20.")
|
|
4736
|
+
};
|
|
4737
|
+
var GoogleAdsPageIntelInputSchema = {
|
|
4738
|
+
advertiserId: import_zod3.z.string().optional().describe("Google Ads Transparency advertiser ID (starts with AR...). Use one returned by google_ads_search; do not construct one yourself."),
|
|
4739
|
+
domain: import_zod3.z.string().optional().describe("A domain (e.g. getviktor.com) whose primary advertiser to inspect when advertiserId is unknown. One of advertiserId or domain is required."),
|
|
4740
|
+
region: import_zod3.z.string().length(2).default("US").describe("Two-letter region code for where the ads are shown. Default US."),
|
|
4741
|
+
maxAds: import_zod3.z.number().int().min(1).max(200).default(50).describe("Maximum creatives to inspect and hydrate. Default 50, maximum 200. Prefer 25-50 for focused scans.")
|
|
4742
|
+
};
|
|
4743
|
+
var GoogleAdsTranscribeInputSchema = {
|
|
4744
|
+
videoUrl: import_zod3.z.string().url().describe("Direct googlevideo.com playback URL from a google_ads_page_intel video ad result (the videoUrl field). For YouTube-hosted ads use youtube_transcribe with the returned youtubeVideoId instead.")
|
|
4745
|
+
};
|
|
4492
4746
|
var InstagramProfileContentInputSchema = {
|
|
4493
4747
|
handle: import_zod3.z.string().min(1).optional().describe("Instagram handle, with or without @. Provide handle or url."),
|
|
4494
4748
|
url: import_zod3.z.string().url().optional().describe("Instagram profile URL, e.g. https://www.instagram.com/nasaartemis/. Provide handle or url."),
|
|
@@ -4699,7 +4953,8 @@ var OrganicResultOutput = import_zod3.z.object({
|
|
|
4699
4953
|
});
|
|
4700
4954
|
var AiOverviewOutput = import_zod3.z.object({
|
|
4701
4955
|
detected: import_zod3.z.boolean(),
|
|
4702
|
-
text: NullableString2
|
|
4956
|
+
text: NullableString2,
|
|
4957
|
+
shareUrl: NullableString2.optional()
|
|
4703
4958
|
}).nullable();
|
|
4704
4959
|
var EntityIdsOutput = import_zod3.z.object({
|
|
4705
4960
|
entities: import_zod3.z.array(import_zod3.z.object({
|
|
@@ -4933,6 +5188,40 @@ var FacebookPageIntelOutputSchema = {
|
|
|
4933
5188
|
variations: import_zod3.z.number().int().nullable()
|
|
4934
5189
|
}))
|
|
4935
5190
|
};
|
|
5191
|
+
var GoogleAdsSearchOutputSchema = {
|
|
5192
|
+
query: import_zod3.z.string(),
|
|
5193
|
+
region: import_zod3.z.string(),
|
|
5194
|
+
advertiserCount: import_zod3.z.number().int().min(0),
|
|
5195
|
+
advertisers: import_zod3.z.array(import_zod3.z.object({
|
|
5196
|
+
advertiserId: NullableString2,
|
|
5197
|
+
name: NullableString2,
|
|
5198
|
+
domain: NullableString2,
|
|
5199
|
+
approxAdCount: import_zod3.z.number().int().nullable(),
|
|
5200
|
+
detailUrl: NullableString2
|
|
5201
|
+
}))
|
|
5202
|
+
};
|
|
5203
|
+
var GoogleAdsPageIntelOutputSchema = {
|
|
5204
|
+
advertiserId: NullableString2,
|
|
5205
|
+
advertiserName: NullableString2,
|
|
5206
|
+
domain: NullableString2,
|
|
5207
|
+
region: import_zod3.z.string(),
|
|
5208
|
+
totalCreatives: import_zod3.z.number().int().min(0),
|
|
5209
|
+
videoCount: import_zod3.z.number().int().min(0),
|
|
5210
|
+
imageCount: import_zod3.z.number().int().min(0),
|
|
5211
|
+
textCount: import_zod3.z.number().int().min(0),
|
|
5212
|
+
ads: import_zod3.z.array(import_zod3.z.object({
|
|
5213
|
+
creativeId: NullableString2,
|
|
5214
|
+
advertiserId: NullableString2,
|
|
5215
|
+
format: NullableString2,
|
|
5216
|
+
lastShown: NullableString2,
|
|
5217
|
+
detailUrl: NullableString2,
|
|
5218
|
+
landingDomain: NullableString2,
|
|
5219
|
+
imageUrls: import_zod3.z.array(import_zod3.z.string()),
|
|
5220
|
+
youtubeVideoId: NullableString2,
|
|
5221
|
+
videoUrl: NullableString2,
|
|
5222
|
+
variations: import_zod3.z.number().int().nullable()
|
|
5223
|
+
}))
|
|
5224
|
+
};
|
|
4936
5225
|
var FacebookVideoTranscribeOutputSchema = {
|
|
4937
5226
|
sourceUrl: import_zod3.z.string().url(),
|
|
4938
5227
|
pageUrl: import_zod3.z.string().url(),
|
|
@@ -5075,6 +5364,17 @@ var FacebookAdTranscribeOutputSchema = {
|
|
|
5075
5364
|
videoUrl: import_zod3.z.string().url()
|
|
5076
5365
|
})
|
|
5077
5366
|
};
|
|
5367
|
+
var GoogleAdsTranscribeOutputSchema = {
|
|
5368
|
+
videoUrl: import_zod3.z.string().url(),
|
|
5369
|
+
wordCount: import_zod3.z.number().int().min(0),
|
|
5370
|
+
chunkCount: import_zod3.z.number().int().min(0),
|
|
5371
|
+
durationMs: import_zod3.z.number().nullable(),
|
|
5372
|
+
transcriptText: import_zod3.z.string(),
|
|
5373
|
+
chunks: import_zod3.z.array(TranscriptChunkOutput),
|
|
5374
|
+
resolvedInputs: import_zod3.z.object({
|
|
5375
|
+
videoUrl: import_zod3.z.string().url()
|
|
5376
|
+
})
|
|
5377
|
+
};
|
|
5078
5378
|
var CaptureSerpSnapshotOutputSchema = {
|
|
5079
5379
|
schemaVersion: import_zod3.z.literal("serp-intelligence.capture.v1"),
|
|
5080
5380
|
status: import_zod3.z.string(),
|
|
@@ -5571,6 +5871,22 @@ function liveWebToolAnnotations(title) {
|
|
|
5571
5871
|
openWorldHint: true
|
|
5572
5872
|
};
|
|
5573
5873
|
}
|
|
5874
|
+
function registerSerpIntelligenceCaptureTools(server2, executor) {
|
|
5875
|
+
server2.registerTool("capture_serp_snapshot", {
|
|
5876
|
+
title: "SERP Intelligence Snapshot",
|
|
5877
|
+
description: "Capture a structured SERP Intelligence Google snapshot through POST /serp-intelligence/capture, the same product capture path used by Phoenix. Split query from location and infer gl/hl. Routing is automatic \u2014 leave proxyMode unset. Set debug true when investigating location evidence, proxy behavior, CAPTCHA, or capture reliability.",
|
|
5878
|
+
inputSchema: CaptureSerpSnapshotInputSchema,
|
|
5879
|
+
outputSchema: CaptureSerpSnapshotOutputSchema,
|
|
5880
|
+
annotations: liveWebToolAnnotations("SERP Intelligence Snapshot")
|
|
5881
|
+
}, async (input) => formatCaptureSerpSnapshot(await executor.captureSerpSnapshot(input), input));
|
|
5882
|
+
server2.registerTool("capture_serp_page_snapshots", {
|
|
5883
|
+
title: "SERP Intelligence Page Snapshots",
|
|
5884
|
+
description: "Capture public ranking-page evidence through POST /serp-intelligence/page-snapshots, the same product page snapshot path used by Phoenix. Provide urls for simple captures or targets when preserving organic, AI citation, local-pack, configured target, or site-subject source metadata. Private IPs, localhost, file URLs, and internal URLs are rejected by the service. Use timeoutMs for slow pages and debug true for sanitized proxy/browser diagnostics.",
|
|
5885
|
+
inputSchema: CaptureSerpPageSnapshotsInputSchema,
|
|
5886
|
+
outputSchema: CaptureSerpPageSnapshotsOutputSchema,
|
|
5887
|
+
annotations: liveWebToolAnnotations("SERP Intelligence Page Snapshots")
|
|
5888
|
+
}, async (input) => formatCaptureSerpPageSnapshots(await executor.captureSerpPageSnapshots(input), input));
|
|
5889
|
+
}
|
|
5574
5890
|
function localPlanningToolAnnotations(title) {
|
|
5575
5891
|
return {
|
|
5576
5892
|
title,
|
|
@@ -5703,6 +6019,27 @@ function registerPaaExtractorMcpTools(server2, executor, options = {}) {
|
|
|
5703
6019
|
outputSchema: FacebookAdTranscribeOutputSchema,
|
|
5704
6020
|
annotations: liveWebToolAnnotations("Facebook Ad Transcription")
|
|
5705
6021
|
}, async (input) => formatFacebookAdTranscribe(await executor.facebookAdTranscribe(input), input));
|
|
6022
|
+
server2.registerTool("google_ads_search", {
|
|
6023
|
+
title: "Google Ads Transparency Search",
|
|
6024
|
+
description: withReportNote("Search the Google Ads Transparency Center to find advertisers running Google ads (Search, Display, YouTube) by domain or brand/advertiser name. Pass a domain like getviktor.com to see every advertiser account pointing ads at that site, or an advertiser name. Returns advertisers with their advertiser ID and an approximate ad count. Use this to discover an advertiserId, then pass it (or the domain) to google_ads_page_intel for the actual ad creatives."),
|
|
6025
|
+
inputSchema: GoogleAdsSearchInputSchema,
|
|
6026
|
+
outputSchema: GoogleAdsSearchOutputSchema,
|
|
6027
|
+
annotations: liveWebToolAnnotations("Google Ads Transparency Search")
|
|
6028
|
+
}, async (input) => formatGoogleAdsSearch(await executor.googleAdsSearch(input), input));
|
|
6029
|
+
server2.registerTool("google_ads_page_intel", {
|
|
6030
|
+
title: "Google Ads Advertiser Intel",
|
|
6031
|
+
description: withReportNote("Harvest an advertiser's ads from the Google Ads Transparency Center when the user wants a competitor's Google/YouTube ad creatives, copy angles, image URLs, or video ads. Accepts an advertiserId (from google_ads_search) or a domain directly. Returns each creative's format (text/image/video), image URLs, landing info, a detail URL, and \u2014 for video ads \u2014 the YouTube video ID and/or a direct video URL ready for transcription. Use google_ads_search first when you only have a brand name. Transcribe video ads with google_ads_transcribe (video URL) or youtube_transcribe (YouTube ID)."),
|
|
6032
|
+
inputSchema: GoogleAdsPageIntelInputSchema,
|
|
6033
|
+
outputSchema: GoogleAdsPageIntelOutputSchema,
|
|
6034
|
+
annotations: liveWebToolAnnotations("Google Ads Advertiser Intel")
|
|
6035
|
+
}, async (input) => formatGoogleAdsPageIntel(await executor.googleAdsPageIntel(input), input));
|
|
6036
|
+
server2.registerTool("google_ads_transcribe", {
|
|
6037
|
+
title: "Google Ad Video Transcription",
|
|
6038
|
+
description: "Transcribe audio from a Google video ad. Use this when google_ads_page_intel returned a direct videoUrl (a googlevideo.com playback URL) for a video creative and the user asks what the ad says or wants its claims/messaging. Returns full transcript, timestamped chunks, and word count. For YouTube-hosted ads, pass the returned youtubeVideoId to youtube_transcribe instead; run google_ads_page_intel first when you only have an advertiser/domain.",
|
|
6039
|
+
inputSchema: GoogleAdsTranscribeInputSchema,
|
|
6040
|
+
outputSchema: GoogleAdsTranscribeOutputSchema,
|
|
6041
|
+
annotations: liveWebToolAnnotations("Google Ad Video Transcription")
|
|
6042
|
+
}, async (input) => formatGoogleAdsTranscribe(await executor.googleAdsTranscribe(input), input));
|
|
5706
6043
|
server2.registerTool("facebook_video_transcribe", {
|
|
5707
6044
|
title: "Facebook Organic Video Transcription",
|
|
5708
6045
|
description: withReportNote("Transcribe audio from an organic Facebook reel, video, watch, post, or share URL, including fb.watch links. Use this when the user pastes a normal Facebook video page URL and wants the transcript or downloadable MP4. Renders the Facebook page in a browser, selects the best matching public Facebook CDN MP4 URL from page state, then returns sourceUrl, resolved pageUrl, videoId, ownerName, selectedQuality, bitrate, videoDurationSec, extracted MP4 URL, full transcript, and timestamped chunks."),
|
|
@@ -5941,7 +6278,9 @@ if (!apiKey) {
|
|
|
5941
6278
|
var baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() || process.env.MCP_BASE_URL?.trim() || "https://mcpscraper.dev";
|
|
5942
6279
|
var consoleBaseUrl = process.env.BROWSER_AGENT_CONSOLE_URL?.trim() || baseUrl;
|
|
5943
6280
|
var server = new import_mcp3.McpServer({ name: "mcp-scraper", version: PACKAGE_VERSION }, { instructions: SERVER_INSTRUCTIONS });
|
|
5944
|
-
|
|
6281
|
+
var httpExecutor = new HttpMcpToolExecutor(baseUrl, apiKey);
|
|
6282
|
+
registerPaaExtractorMcpTools(server, httpExecutor);
|
|
6283
|
+
registerSerpIntelligenceCaptureTools(server, httpExecutor);
|
|
5945
6284
|
registerBrowserAgentMcpTools(server, { baseUrl, apiKey, consoleBaseUrl });
|
|
5946
6285
|
var transport = new import_stdio.StdioServerTransport();
|
|
5947
6286
|
async function main() {
|