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.
Files changed (45) hide show
  1. package/dist/bin/api-server.cjs +2475 -1154
  2. package/dist/bin/api-server.cjs.map +1 -1
  3. package/dist/bin/api-server.js +3 -3
  4. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  5. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  6. package/dist/bin/mcp-scraper-cli.js +1 -1
  7. package/dist/bin/mcp-scraper-install.cjs +1 -1
  8. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  9. package/dist/bin/mcp-scraper-install.js +1 -1
  10. package/dist/bin/mcp-stdio-server.cjs +346 -7
  11. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  12. package/dist/bin/mcp-stdio-server.js +7 -4
  13. package/dist/bin/mcp-stdio-server.js.map +1 -1
  14. package/dist/bin/paa-harvest.cjs +109 -5
  15. package/dist/bin/paa-harvest.cjs.map +1 -1
  16. package/dist/bin/paa-harvest.js +2 -2
  17. package/dist/{chunk-TRNSXR46.js → chunk-DVRPXPYH.js} +22 -3
  18. package/dist/chunk-DVRPXPYH.js.map +1 -0
  19. package/dist/{chunk-QSSH4RCX.js → chunk-GMTS35L6.js} +111 -7
  20. package/dist/chunk-GMTS35L6.js.map +1 -0
  21. package/dist/chunk-I5EFBTYE.js +7 -0
  22. package/dist/chunk-I5EFBTYE.js.map +1 -0
  23. package/dist/{chunk-H74L743B.js → chunk-KJWNVQFL.js} +39 -2
  24. package/dist/chunk-KJWNVQFL.js.map +1 -0
  25. package/dist/{chunk-6KHGYJIT.js → chunk-QLQYNE7E.js} +255 -14
  26. package/dist/chunk-QLQYNE7E.js.map +1 -0
  27. package/dist/{db-FP2ABUU4.js → db-LYQENFPW.js} +2 -2
  28. package/dist/index.cjs +109 -5
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +8 -0
  31. package/dist/index.d.ts +8 -0
  32. package/dist/index.js +2 -2
  33. package/dist/{server-KUNOK47B.js → server-HB7WJO3N.js} +1155 -274
  34. package/dist/server-HB7WJO3N.js.map +1 -0
  35. package/dist/{worker-NV4GTIRG.js → worker-TL4J65DE.js} +4 -4
  36. package/package.json +1 -1
  37. package/dist/chunk-6KHGYJIT.js.map +0 -1
  38. package/dist/chunk-DYV72F6A.js +0 -7
  39. package/dist/chunk-DYV72F6A.js.map +0 -1
  40. package/dist/chunk-H74L743B.js.map +0 -1
  41. package/dist/chunk-QSSH4RCX.js.map +0 -1
  42. package/dist/chunk-TRNSXR46.js.map +0 -1
  43. package/dist/server-KUNOK47B.js.map +0 -1
  44. /package/dist/{db-FP2ABUU4.js.map → db-LYQENFPW.js.map} +0 -0
  45. /package/dist/{worker-NV4GTIRG.js.map → worker-TL4J65DE.js.map} +0 -0
@@ -9,7 +9,7 @@ import {
9
9
  } from "../chunk-NNEIXK5L.js";
10
10
  import {
11
11
  PACKAGE_VERSION
12
- } from "../chunk-DYV72F6A.js";
12
+ } from "../chunk-I5EFBTYE.js";
13
13
  import "../chunk-K443GQY5.js";
14
14
  import "../chunk-BP27CZ5Q.js";
15
15
 
@@ -101,7 +101,7 @@ function renderInstallTerminal(options) {
101
101
  }
102
102
 
103
103
  // src/version.ts
104
- var PACKAGE_VERSION = "0.3.27";
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.27'\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":[]}
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-DYV72F6A.js";
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.27";
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
- registerPaaExtractorMcpTools(server, new HttpMcpToolExecutor(baseUrl, apiKey));
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() {