mcp-scraper 0.3.1 → 0.3.3

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 (51) hide show
  1. package/README.md +4 -1
  2. package/dist/bin/api-server.cjs +2050 -832
  3. package/dist/bin/api-server.cjs.map +1 -1
  4. package/dist/bin/api-server.js +2 -2
  5. package/dist/bin/browser-agent-stdio-server.cjs +1 -1
  6. package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
  7. package/dist/bin/browser-agent-stdio-server.js +2 -2
  8. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  9. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  10. package/dist/bin/mcp-scraper-cli.js +1 -1
  11. package/dist/bin/mcp-scraper-combined-stdio-server.cjs +351 -4
  12. package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
  13. package/dist/bin/mcp-scraper-combined-stdio-server.js +4 -4
  14. package/dist/bin/mcp-scraper-install.cjs +3 -3
  15. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  16. package/dist/bin/mcp-scraper-install.js +2 -2
  17. package/dist/bin/mcp-stdio-server.cjs +349 -2
  18. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  19. package/dist/bin/mcp-stdio-server.js +2 -2
  20. package/dist/bin/paa-harvest.cjs +3 -1
  21. package/dist/bin/paa-harvest.cjs.map +1 -1
  22. package/dist/bin/paa-harvest.js +1 -1
  23. package/dist/{chunk-SXTXMFEQ.js → chunk-AZ5PKU4F.js} +34 -1
  24. package/dist/chunk-AZ5PKU4F.js.map +1 -0
  25. package/dist/{chunk-Q4STSM63.js → chunk-C3FGVJWH.js} +3 -3
  26. package/dist/chunk-C3FGVJWH.js.map +1 -0
  27. package/dist/{chunk-55T4SRLJ.js → chunk-MGWGZBL5.js} +350 -3
  28. package/dist/chunk-MGWGZBL5.js.map +1 -0
  29. package/dist/chunk-PVXDEREW.js +7 -0
  30. package/dist/chunk-PVXDEREW.js.map +1 -0
  31. package/dist/{chunk-LICHCMV6.js → chunk-RLBJ3QNC.js} +2 -2
  32. package/dist/{chunk-IPW4LFOT.js → chunk-UWSG3C5J.js} +4 -2
  33. package/dist/chunk-UWSG3C5J.js.map +1 -0
  34. package/dist/index.cjs +3 -1
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.js +1 -1
  37. package/dist/{server-AXPNL2RV.js → server-D3XHIEQN.js} +997 -253
  38. package/dist/server-D3XHIEQN.js.map +1 -0
  39. package/dist/{worker-SLQ375UG.js → worker-56IXWOQU.js} +3 -3
  40. package/docs/mcp-tool-craft-lint.generated.md +5 -2
  41. package/docs/mcp-tool-manifest.generated.json +80 -5
  42. package/package.json +1 -1
  43. package/dist/chunk-55T4SRLJ.js.map +0 -1
  44. package/dist/chunk-D4JDGKOV.js +0 -7
  45. package/dist/chunk-D4JDGKOV.js.map +0 -1
  46. package/dist/chunk-IPW4LFOT.js.map +0 -1
  47. package/dist/chunk-Q4STSM63.js.map +0 -1
  48. package/dist/chunk-SXTXMFEQ.js.map +0 -1
  49. package/dist/server-AXPNL2RV.js.map +0 -1
  50. /package/dist/{chunk-LICHCMV6.js.map → chunk-RLBJ3QNC.js.map} +0 -0
  51. /package/dist/{worker-SLQ375UG.js.map → worker-56IXWOQU.js.map} +0 -0
@@ -2,21 +2,21 @@
2
2
  import {
3
3
  HttpMcpToolExecutor,
4
4
  registerPaaExtractorMcpTools
5
- } from "../chunk-55T4SRLJ.js";
5
+ } from "../chunk-MGWGZBL5.js";
6
6
  import "../chunk-M2S27J6Z.js";
7
7
  import {
8
8
  registerBrowserAgentMcpTools
9
- } from "../chunk-LICHCMV6.js";
9
+ } from "../chunk-RLBJ3QNC.js";
10
10
  import "../chunk-NXRWFOEZ.js";
11
11
  import "../chunk-WN7PBKMV.js";
12
12
  import {
13
13
  renderInstallTerminal
14
- } from "../chunk-Q4STSM63.js";
14
+ } from "../chunk-C3FGVJWH.js";
15
15
  import "../chunk-LFATOGDF.js";
16
16
  import "../chunk-DUEW4EOO.js";
17
17
  import {
18
18
  PACKAGE_VERSION
19
- } from "../chunk-D4JDGKOV.js";
19
+ } from "../chunk-PVXDEREW.js";
20
20
 
21
21
  // bin/mcp-scraper-combined-stdio-server.ts
22
22
  import { readFileSync } from "fs";
@@ -53,11 +53,11 @@ function renderInstallTerminal(options) {
53
53
  "1/1 install surfaces ready",
54
54
  colorize("Newest: local Chrome profile mode for browser_open, browser_profile_import/sync, and one-server MCP installs.", "lime", color),
55
55
  "",
56
- `${colorize("Tools", "cyan", color)} ${colorize("(43 MCP tools)", "muted", color)}`,
56
+ `${colorize("Tools", "cyan", color)} ${colorize("(46 MCP tools)", "muted", color)}`,
57
57
  toolRow("search", ["harvest_paa", "search_serp", "maps_search", "maps_place_intel"], color),
58
58
  toolRow("extract", ["extract_url", "map_site_urls", "extract_site", "directory_workflow"], color),
59
59
  toolRow("build", ["rank_tracker_blueprint", "cron plan", "database prompt"], color),
60
- toolRow("media", ["youtube_harvest", "youtube_transcribe", "facebook_ad_search", "facebook_page_intel", "facebook_ad_transcribe", "facebook_video_transcribe"], color),
60
+ toolRow("media", ["youtube_harvest", "youtube_transcribe", "facebook_ad_search", "facebook_page_intel", "facebook_ad_transcribe", "facebook_video_transcribe", "instagram_profile_content", "instagram_media_download"], color),
61
61
  toolRow("browser", ["browser_open", "browser_profile_import", "browser_profile_sync", "browser_screenshot", "browser_read", "browser_locate", "browser_replay_mark", "browser_replay_annotate"], color),
62
62
  toolRow("account", ["credits_info", "local reports", "MCP resources"], color),
63
63
  `${colorize("Workflows", "cyan", color)} ${colorize("(MCP + CLI + API)", "muted", color)}`,
@@ -101,7 +101,7 @@ function renderInstallTerminal(options) {
101
101
  }
102
102
 
103
103
  // src/version.ts
104
- var PACKAGE_VERSION = "0.3.1";
104
+ var PACKAGE_VERSION = "0.3.3";
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-combined\"]',\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: local Chrome profile mode for browser_open, browser_profile_import/sync, and one-server MCP installs.', 'lime', color),\n '',\n `${colorize('Tools', 'cyan', color)} ${colorize('(43 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', 'directory_workflow'], color),\n toolRow('build', ['rank_tracker_blueprint', 'cron plan', 'database prompt'], color),\n toolRow('media', ['youtube_harvest', 'youtube_transcribe', 'facebook_ad_search', 'facebook_page_intel', 'facebook_ad_transcribe', 'facebook_video_transcribe'], color),\n toolRow('browser', ['browser_open', 'browser_profile_import', 'browser_profile_sync', 'browser_screenshot', 'browser_read', 'browser_locate', 'browser_replay_mark', 'browser_replay_annotate'], color),\n toolRow('account', ['credits_info', 'local 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', 'local 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-combined 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 'Local Chrome mode: mcp-scraper-cli browser import-chrome --email you@example.com --name chrome-default',\n 'Then: mcp-scraper-cli agent install claude --apply --browser-mode local --browser-profile chrome-default',\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-combined 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('Local Chrome profile setup', 'amber', color),\n 'npx -y -p mcp-scraper@latest mcp-scraper-cli browser profiles --email you@example.com',\n 'npx -y -p mcp-scraper@latest mcp-scraper-cli browser import-chrome --email you@example.com --name chrome-default',\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-combined 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'\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-combined 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-combined\"]',\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: local Chrome profile mode for browser_open, browser_profile_import/sync, and one-server MCP installs.', 'lime', color),\n '',\n `${colorize('Tools', 'cyan', color)} ${colorize('(46 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', 'directory_workflow'], color),\n toolRow('build', ['rank_tracker_blueprint', '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'], color),\n toolRow('browser', ['browser_open', 'browser_profile_import', 'browser_profile_sync', 'browser_screenshot', 'browser_read', 'browser_locate', 'browser_replay_mark', 'browser_replay_annotate'], color),\n toolRow('account', ['credits_info', 'local 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', 'local 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-combined 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 'Local Chrome mode: mcp-scraper-cli browser import-chrome --email you@example.com --name chrome-default',\n 'Then: mcp-scraper-cli agent install claude --apply --browser-mode local --browser-profile chrome-default',\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-combined 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('Local Chrome profile setup', 'amber', color),\n 'npx -y -p mcp-scraper@latest mcp-scraper-cli browser profiles --email you@example.com',\n 'npx -y -p mcp-scraper@latest mcp-scraper-cli browser import-chrome --email you@example.com --name chrome-default',\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-combined 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.3'\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-combined 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":";;;;AAQA,IAAM,QAAwC;AAAA,EAC5C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AAAA,EACP,MAAM;AACR;AAEA,SAAS,SAAS,OAAe,MAAY,SAA0B;AACrE,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,GAAG,MAAM,IAAI,CAAC,GAAG,KAAK,GAAG,MAAM,KAAK;AAC7C;AAEA,SAAS,QAAQ,OAAe,OAAiB,SAA0B;AACzE,QAAM,SAAS,MAAM,OAAO,GAAG,GAAG;AAClC,SAAO,KAAK,SAAS,QAAQ,SAAS,OAAO,CAAC,IAAI,MAAM,KAAK,SAAS,SAAS,SAAS,OAAO,CAAC,CAAC;AACnG;AAEO,SAAS,sBAAsB,SAAyC;AAC7E,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,cAAc,QAAQ,mBAAmB,yBAAyB;AACxE,QAAM,QAAQ,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcrB,QAAM,gBAAgB;AAAA,IACpB,uBAAuB,WAAW;AAAA,IAClC;AAAA,EACF,EAAE,KAAK,IAAI;AAEX,QAAM,cAAc;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA,kCAAkC,WAAW;AAAA,EAC/C,EAAE,KAAK,IAAI;AAEX,SAAO;AAAA,IACL,SAAS,gBAAgB,QAAQ,OAAO,IAAI,QAAQ,KAAK;AAAA,IACzD,SAAS,yBAAyB,SAAS,KAAK;AAAA,IAChD,SAAS,OAAO,SAAS,KAAK;AAAA,IAC9B,GAAG,SAAS,qBAAqB,QAAQ,KAAK,CAAC,SAAS,QAAQ,OAAO;AAAA,IACvE;AAAA,IACA,SAAS,iHAAiH,QAAQ,KAAK;AAAA,IACvI;AAAA,IACA,GAAG,SAAS,SAAS,QAAQ,KAAK,CAAC,KAAK,SAAS,kBAAkB,SAAS,KAAK,CAAC;AAAA,IAClF,QAAQ,UAAU,CAAC,eAAe,eAAe,eAAe,kBAAkB,GAAG,KAAK;AAAA,IAC1F,QAAQ,WAAW,CAAC,eAAe,iBAAiB,gBAAgB,oBAAoB,GAAG,KAAK;AAAA,IAChG,QAAQ,SAAS,CAAC,0BAA0B,aAAa,iBAAiB,GAAG,KAAK;AAAA,IAClF,QAAQ,SAAS,CAAC,mBAAmB,sBAAsB,sBAAsB,uBAAuB,0BAA0B,6BAA6B,6BAA6B,0BAA0B,GAAG,KAAK;AAAA,IAC9N,QAAQ,WAAW,CAAC,gBAAgB,0BAA0B,wBAAwB,sBAAsB,gBAAgB,kBAAkB,uBAAuB,yBAAyB,GAAG,KAAK;AAAA,IACtM,QAAQ,WAAW,CAAC,gBAAgB,iBAAiB,eAAe,GAAG,KAAK;AAAA,IAC5E,GAAG,SAAS,aAAa,QAAQ,KAAK,CAAC,KAAK,SAAS,qBAAqB,SAAS,KAAK,CAAC;AAAA,IACzF,QAAQ,SAAS,CAAC,iBAAiB,oBAAoB,gBAAgB,iBAAiB,mBAAmB,wBAAwB,GAAG,KAAK;AAAA,IAC3I,QAAQ,OAAO,CAAC,aAAa,gBAAgB,eAAe,uBAAuB,kBAAkB,gBAAgB,GAAG,KAAK;AAAA,IAC7H;AAAA,IACA,SAAS,eAAe,SAAS,KAAK;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG,SAAS,UAAU,QAAQ,KAAK,CAAC;AAAA,IACpC;AAAA,IACA,SAAS,gBAAgB,SAAS,KAAK;AAAA,IACvC;AAAA,IACA;AAAA,IACA,SAAS,8BAA8B,SAAS,KAAK;AAAA,IACrD;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,iCAAiC,SAAS,KAAK;AAAA,IACxD;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,gBAAgB,SAAS,KAAK;AAAA,IACvC;AAAA,IACA;AAAA,IACA,SAAS,4BAA4B,SAAS,KAAK;AAAA,IACnD;AAAA,IACA;AAAA,IACA,SAAS,gBAAgB,SAAS,KAAK;AAAA,IACvC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;;;AC9GO,IAAM,kBAAkB;;;ACI/B,IAAM,UACJ,QAAQ,KAAK,SAAS,YAAY,KAClC,QAAQ,IAAI,aAAa,UACzB,QAAQ,IAAI,gBAAgB,OAC5B,CAAC,QAAQ,OAAO;AAElB,IAAM,OAAO,QAAQ,KAAK,SAAS,QAAQ,KAAK,QAAQ,KAAK,SAAS,IAAI;AAC1E,IAAI,MAAM;AACR,UAAQ,OAAO,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI,CAAC;AACZ,UAAQ,KAAK,CAAC;AAChB;AAEA,QAAQ,OAAO,MAAM,sBAAsB;AAAA,EACzC,SAAS;AAAA,EACT,OAAO,CAAC;AAAA,EACR,kBAAkB,QAAQ,QAAQ,IAAI,qBAAqB,KAAK,CAAC;AACnE,CAAC,CAAC;","names":[]}
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  renderInstallTerminal
4
- } from "../chunk-Q4STSM63.js";
4
+ } from "../chunk-C3FGVJWH.js";
5
5
  import {
6
6
  PACKAGE_VERSION
7
- } from "../chunk-D4JDGKOV.js";
7
+ } from "../chunk-PVXDEREW.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;
@@ -196,6 +196,12 @@ var HttpMcpToolExecutor = class {
196
196
  facebookVideoTranscribe(input) {
197
197
  return this.call("/facebook/video-transcribe", input, this.httpTimeoutOverrideMs ?? 24e4);
198
198
  }
199
+ instagramProfileContent(input) {
200
+ return this.call("/instagram/profile-content", input, this.httpTimeoutOverrideMs ?? 24e4);
201
+ }
202
+ instagramMediaDownload(input) {
203
+ return this.call("/instagram/media-download", input, this.httpTimeoutOverrideMs ?? 3e5);
204
+ }
199
205
  mapsPlaceIntel(input) {
200
206
  return this.call("/maps/place", input);
201
207
  }
@@ -249,7 +255,7 @@ var import_node_fs2 = require("fs");
249
255
  var import_node_path2 = require("path");
250
256
 
251
257
  // src/version.ts
252
- var PACKAGE_VERSION = "0.3.1";
258
+ var PACKAGE_VERSION = "0.3.3";
253
259
 
254
260
  // src/mcp/mcp-response-formatter.ts
255
261
  var import_node_fs = require("fs");
@@ -731,7 +737,9 @@ ${headingSection}${kpoSection}${brandingSection}${bodySection}${screenshotSectio
731
737
  entityTypes: kpo?.type ?? [],
732
738
  napScore: kpo?.napScore ?? null,
733
739
  missingSchemaFields: kpo?.missingFields ?? [],
734
- screenshotSaved: screenshotPath ?? null
740
+ screenshotSaved: screenshotPath ?? null,
741
+ branding: branding ?? null,
742
+ mediaAssets: media?.assets ?? null
735
743
  };
736
744
  if (screenshotMeta?.base64) {
737
745
  return {
@@ -1703,6 +1711,216 @@ ${chunkRows}` : "",
1703
1711
  }
1704
1712
  };
1705
1713
  }
1714
+ function structuredInstagramBrowser(raw) {
1715
+ const browser = raw && typeof raw === "object" ? raw : {};
1716
+ const mode = browser.mode === "local" ? "local" : "hosted";
1717
+ const requestedMode = browser.requestedMode === "local" || browser.requestedMode === "hosted" || browser.requestedMode === "auto" ? browser.requestedMode : "auto";
1718
+ const profileSource = browser.profileSource === "managed_profile" || browser.profileSource === "direct_profile_dir" ? browser.profileSource : "hosted";
1719
+ return {
1720
+ mode,
1721
+ requestedMode,
1722
+ profileName: typeof browser.profileName === "string" ? browser.profileName : null,
1723
+ profileSource,
1724
+ profileDirConfigured: browser.profileDirConfigured === true,
1725
+ executablePathConfigured: browser.executablePathConfigured === true
1726
+ };
1727
+ }
1728
+ function structuredInstagramPagination(raw, input) {
1729
+ const pagination = raw && typeof raw === "object" ? raw : {};
1730
+ const stages = Array.isArray(pagination.stages) ? pagination.stages : [];
1731
+ const stoppedReason = typeof pagination.stoppedReason === "string" && ["max_items", "reported_post_count", "stable_scrolls", "max_scrolls", "no_scrolls"].includes(pagination.stoppedReason) ? pagination.stoppedReason : "no_scrolls";
1732
+ return {
1733
+ maxItems: typeof pagination.maxItems === "number" ? pagination.maxItems : Number(input.maxItems ?? 50),
1734
+ maxScrolls: typeof pagination.maxScrolls === "number" ? pagination.maxScrolls : Number(input.maxScrolls ?? 10),
1735
+ attemptedScrolls: typeof pagination.attemptedScrolls === "number" ? pagination.attemptedScrolls : 0,
1736
+ stableScrolls: typeof pagination.stableScrolls === "number" ? pagination.stableScrolls : 0,
1737
+ stableScrollLimit: typeof pagination.stableScrollLimit === "number" ? pagination.stableScrollLimit : Number(input.stableScrollLimit ?? 4),
1738
+ scrollDelayMs: typeof pagination.scrollDelayMs === "number" ? pagination.scrollDelayMs : Number(input.scrollDelayMs ?? 1200),
1739
+ reachedMaxItems: pagination.reachedMaxItems === true,
1740
+ reachedReportedPostCount: pagination.reachedReportedPostCount === true,
1741
+ finalScrollHeight: typeof pagination.finalScrollHeight === "number" ? pagination.finalScrollHeight : null,
1742
+ stoppedReason,
1743
+ stages: stages.map((stage) => {
1744
+ const row = stage && typeof stage === "object" ? stage : {};
1745
+ return {
1746
+ stage: String(row.stage ?? ""),
1747
+ itemCount: typeof row.itemCount === "number" ? row.itemCount : 0,
1748
+ addedCount: typeof row.addedCount === "number" ? row.addedCount : 0,
1749
+ scrollY: typeof row.scrollY === "number" ? row.scrollY : null,
1750
+ scrollHeight: typeof row.scrollHeight === "number" ? row.scrollHeight : null
1751
+ };
1752
+ })
1753
+ };
1754
+ }
1755
+ function formatInstagramProfileContent(raw, input) {
1756
+ const parsed = parseData(raw);
1757
+ if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
1758
+ const d = parsed.data;
1759
+ const items = Array.isArray(d.items) ? d.items : [];
1760
+ const typeCounts = d.typeCounts;
1761
+ const limitations = Array.isArray(d.limitations) ? d.limitations.map(String) : [];
1762
+ const browser = structuredInstagramBrowser(d.browser);
1763
+ const pagination = structuredInstagramPagination(d.pagination, input);
1764
+ const itemRows = items.slice(0, 100).map(
1765
+ (item, i) => `| ${i + 1} | ${item.type} | \`${item.shortcode}\` | ${item.url} | ${cell(item.firstSeenStage ?? "")} |`
1766
+ ).join("\n");
1767
+ const browserLabel = browser.mode === "local" ? `local profile ${browser.profileName ? `\`${browser.profileName}\`` : "(unnamed)"}` : "hosted browser";
1768
+ const full = [
1769
+ `# Instagram Profile Content: ${d.handle ?? input.handle ?? input.url ?? "profile"}`,
1770
+ `**Collected:** ${items.length} items \xB7 posts ${typeCounts?.post ?? 0} \xB7 reels ${typeCounts?.reel ?? 0} \xB7 tv ${typeCounts?.tv ?? 0}`,
1771
+ `**Browser:** ${browserLabel}`,
1772
+ `**Pagination:** ${pagination.attemptedScrolls} scrolls \xB7 stopped: ${pagination.stoppedReason}`,
1773
+ d.reportedPostCountText ? `**Profile count:** ${d.reportedPostCountText}` : "",
1774
+ d.followerCountText ? `**Followers:** ${d.followerCountText}` : "",
1775
+ `
1776
+ ## Content Links
1777
+ | # | Type | Shortcode | URL | First Seen |
1778
+ |---|------|-----------|-----|------------|
1779
+ ${itemRows || "| \u2014 | \u2014 | \u2014 | \u2014 | \u2014 |"}`,
1780
+ limitations.length ? `
1781
+ ## Limits
1782
+ ${limitations.map((l) => `- ${l}`).join("\n")}` : "",
1783
+ `
1784
+ ---
1785
+ \u{1F4A1} Use \`instagram_media_download\` with one of these URLs to download text, image, reel tracks, and optional transcript.`
1786
+ ].filter(Boolean).join("\n");
1787
+ return {
1788
+ ...oneBlock(full),
1789
+ structuredContent: {
1790
+ handle: String(d.handle ?? input.handle ?? ""),
1791
+ profileUrl: String(d.profileUrl ?? input.url ?? ""),
1792
+ pageUrl: String(d.pageUrl ?? d.profileUrl ?? input.url ?? ""),
1793
+ browser,
1794
+ profileName: typeof d.profileName === "string" ? d.profileName : null,
1795
+ reportedPostCount: typeof d.reportedPostCount === "number" ? d.reportedPostCount : null,
1796
+ reportedPostCountText: typeof d.reportedPostCountText === "string" ? d.reportedPostCountText : null,
1797
+ followerCountText: typeof d.followerCountText === "string" ? d.followerCountText : null,
1798
+ followingCountText: typeof d.followingCountText === "string" ? d.followingCountText : null,
1799
+ collectedContentCount: items.length,
1800
+ typeCounts: {
1801
+ post: Number(typeCounts?.post ?? 0),
1802
+ reel: Number(typeCounts?.reel ?? 0),
1803
+ tv: Number(typeCounts?.tv ?? 0)
1804
+ },
1805
+ pagination,
1806
+ limited: d.limited === true,
1807
+ limitations,
1808
+ items: items.map((item) => ({
1809
+ url: String(item.url ?? ""),
1810
+ type: item.type,
1811
+ shortcode: String(item.shortcode ?? ""),
1812
+ anchorText: item.anchorText ?? null,
1813
+ firstSeenStage: String(item.firstSeenStage ?? "")
1814
+ }))
1815
+ }
1816
+ };
1817
+ }
1818
+ function structuredInstagramTrack(track) {
1819
+ if (!track) return null;
1820
+ return {
1821
+ url: String(track.url ?? ""),
1822
+ streamType: track.streamType ?? "unknown",
1823
+ bitrate: typeof track.bitrate === "number" ? track.bitrate : null,
1824
+ durationSec: typeof track.durationSec === "number" ? track.durationSec : null,
1825
+ vencodeTag: track.vencodeTag ?? null,
1826
+ width: typeof track.width === "number" ? track.width : null,
1827
+ height: typeof track.height === "number" ? track.height : null
1828
+ };
1829
+ }
1830
+ function formatInstagramMediaDownload(raw, input) {
1831
+ const parsed = parseData(raw);
1832
+ if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
1833
+ const d = parsed.data;
1834
+ const tracks = Array.isArray(d.tracks) ? d.tracks : [];
1835
+ const downloads = Array.isArray(d.downloads) ? d.downloads : [];
1836
+ const warnings = Array.isArray(d.warnings) ? d.warnings.map(String) : [];
1837
+ const limitations = Array.isArray(d.limitations) ? d.limitations.map(String) : [];
1838
+ const transcript = d.transcript;
1839
+ const transcriptText = transcript?.text ?? "";
1840
+ const chunks = transcript?.chunks ?? [];
1841
+ const browser = structuredInstagramBrowser(d.browser);
1842
+ const browserLabel = browser.mode === "local" ? `local profile ${browser.profileName ? `\`${browser.profileName}\`` : "(unnamed)"}` : "hosted browser";
1843
+ const downloadRows = downloads.map((download, i) => {
1844
+ const status = download.error ? `error: ${cell(download.error)}` : `${download.sizeBytes ?? 0} bytes`;
1845
+ return `| ${i + 1} | ${download.kind} | ${download.savedPath ? `\`${download.savedPath}\`` : "\u2014"} | ${status} |`;
1846
+ }).join("\n");
1847
+ const trackRows = tracks.slice(0, 20).map(
1848
+ (track, i) => `| ${i + 1} | ${track.streamType} | ${track.bitrate ?? "\u2014"} | ${track.durationSec ?? "\u2014"} | ${cell(track.vencodeTag ?? "")} |`
1849
+ ).join("\n");
1850
+ const full = [
1851
+ `# Instagram Media Download`,
1852
+ `**URL:** ${d.pageUrl ?? input.url}`,
1853
+ `**Browser:** ${browserLabel}`,
1854
+ d.ownerName ? `**Owner:** ${d.ownerName}` : "",
1855
+ d.shortcode ? `**Shortcode:** \`${d.shortcode}\`` : "",
1856
+ d.caption ? `
1857
+ ## Caption
1858
+ ${truncate(String(d.caption), 1200)}` : "",
1859
+ d.imageUrl ? `
1860
+ ## Image
1861
+ ${d.imageUrl}` : "",
1862
+ tracks.length ? `
1863
+ ## Media Tracks
1864
+ | # | Type | Bitrate | Duration | Tag |
1865
+ |---|------|---------|----------|-----|
1866
+ ${trackRows}` : "\n## Media Tracks\n*None captured.*",
1867
+ downloads.length ? `
1868
+ ## Downloads
1869
+ | # | Kind | File | Status |
1870
+ |---|------|------|--------|
1871
+ ${downloadRows}` : "",
1872
+ d.outputDir ? `
1873
+ **Output directory:** \`${d.outputDir}\`` : "",
1874
+ transcript ? `
1875
+ ## Transcript
1876
+ **${wordCount(transcriptText)} words** \xB7 ${chunks.length} chunks
1877
+
1878
+ ${transcriptText}` : "",
1879
+ warnings.length ? `
1880
+ ## Warnings
1881
+ ${warnings.map((w) => `- ${w}`).join("\n")}` : "",
1882
+ limitations.length ? `
1883
+ ## Limits
1884
+ ${limitations.map((l) => `- ${l}`).join("\n")}` : "",
1885
+ `
1886
+ ---
1887
+ \u{1F4A1} Reels may expose separate video-only and audio-only MP4 tracks. Use the muxed file when present; otherwise use the selected video/audio track URLs or saved files.`
1888
+ ].filter(Boolean).join("\n");
1889
+ return {
1890
+ ...oneBlock(full),
1891
+ structuredContent: {
1892
+ sourceUrl: String(d.sourceUrl ?? input.url),
1893
+ pageUrl: String(d.pageUrl ?? input.url),
1894
+ browser,
1895
+ type: d.type === "post" || d.type === "reel" || d.type === "tv" ? d.type : null,
1896
+ shortcode: typeof d.shortcode === "string" ? d.shortcode : null,
1897
+ ownerName: typeof d.ownerName === "string" ? d.ownerName : null,
1898
+ caption: typeof d.caption === "string" ? d.caption : null,
1899
+ imageUrl: typeof d.imageUrl === "string" ? d.imageUrl : null,
1900
+ trackCount: tracks.length,
1901
+ selectedVideoTrack: structuredInstagramTrack(d.selectedVideoTrack),
1902
+ selectedAudioTrack: structuredInstagramTrack(d.selectedAudioTrack),
1903
+ downloads: downloads.map((download) => ({
1904
+ kind: download.kind,
1905
+ url: download.url ?? null,
1906
+ savedPath: download.savedPath ?? null,
1907
+ sizeBytes: typeof download.sizeBytes === "number" ? download.sizeBytes : null,
1908
+ mimeType: download.mimeType ?? null,
1909
+ error: download.error ?? null
1910
+ })),
1911
+ outputDir: typeof d.outputDir === "string" ? d.outputDir : null,
1912
+ warnings,
1913
+ limitations,
1914
+ transcript: transcript ? {
1915
+ wordCount: wordCount(transcriptText),
1916
+ chunkCount: chunks.length,
1917
+ durationMs: typeof transcript.durationMs === "number" ? transcript.durationMs : null,
1918
+ transcriptText,
1919
+ chunks: structuredTranscriptChunks(chunks)
1920
+ } : null
1921
+ }
1922
+ };
1923
+ }
1706
1924
 
1707
1925
  // src/mcp/mcp-tool-schemas.ts
1708
1926
  var import_zod2 = require("zod");
@@ -1863,6 +2081,26 @@ var FacebookVideoTranscribeInputSchema = {
1863
2081
  url: import_zod2.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."),
1864
2082
  quality: import_zod2.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.")
1865
2083
  };
2084
+ var InstagramProfileContentInputSchema = {
2085
+ handle: import_zod2.z.string().min(1).optional().describe("Instagram handle, with or without @. Provide handle or url. Use only public profiles unless a configured browser profile is available server-side."),
2086
+ url: import_zod2.z.string().url().optional().describe("Instagram profile URL, e.g. https://www.instagram.com/nasaartemis/. Provide handle or url."),
2087
+ maxItems: import_zod2.z.number().int().min(1).max(500).default(50).describe("Maximum profile grid post/reel/tv URLs to collect. Default 50, maximum 500. Use higher values with an authenticated browser profile when the user asks for a fuller archive."),
2088
+ maxScrolls: import_zod2.z.number().int().min(0).max(100).default(10).describe("Maximum pagination scroll attempts. Default 10, maximum 100. Increase when using authenticated Chromium and the profile has a long history."),
2089
+ scrollDelayMs: import_zod2.z.number().int().min(250).max(5e3).default(1200).describe("Delay after each pagination scroll before collecting newly loaded links. Default 1200ms. Increase to 2000-3000ms when Instagram loads slowly."),
2090
+ stableScrollLimit: import_zod2.z.number().int().min(1).max(10).default(4).describe("Stop after this many consecutive scrolls with no new links or scroll progress. Default 4."),
2091
+ browserMode: import_zod2.z.enum(["auto", "hosted", "local"]).default("auto").describe("Browser backend. auto uses the hosted browser unless local mode is configured or browserProfile is passed. local uses an imported/authenticated Chrome profile from browser_profile_import/sync. hosted forces the remote browser path."),
2092
+ browserProfile: import_zod2.z.string().min(1).optional().describe("Managed local browser profile name created by browser_profile_import/sync. Passing this opts the Instagram tool into local authenticated Chromium for this call.")
2093
+ };
2094
+ var InstagramMediaDownloadInputSchema = {
2095
+ url: import_zod2.z.string().url().describe("Instagram post, reel, or tv URL, e.g. https://www.instagram.com/reel/SHORTCODE/. The tool renders the page, extracts text, image metadata, and Instagram CDN media tracks."),
2096
+ mediaTypes: import_zod2.z.array(import_zod2.z.enum(["image", "video", "audio"])).default(["image", "video", "audio"]).describe("Which media types to download when downloadMedia is true. Reels commonly expose separate video-only and audio-only MP4 tracks."),
2097
+ downloadMedia: import_zod2.z.boolean().default(true).describe("Download extracted text/media files to the MCP Scraper output directory when the API server can write files. Always returns extracted media URLs even when false."),
2098
+ downloadAllTracks: import_zod2.z.boolean().default(false).describe("Download every captured Instagram MP4 track instead of only the selected best video and audio tracks. Use false by default to avoid duplicate bitrates."),
2099
+ includeTranscript: import_zod2.z.boolean().default(false).describe("Transcribe the selected audio track when available. This adds transcription cost and may take longer."),
2100
+ mux: import_zod2.z.boolean().default(true).describe("When video and audio tracks are downloaded separately, try to mux them into a single MP4 if ffmpeg is available. Returns separate tracks when muxing is unavailable."),
2101
+ browserMode: import_zod2.z.enum(["auto", "hosted", "local"]).default("auto").describe("Browser backend. auto uses the hosted browser unless local mode is configured or browserProfile is passed. local uses an imported/authenticated Chrome profile from browser_profile_import/sync. hosted forces the remote browser path."),
2102
+ browserProfile: import_zod2.z.string().min(1).optional().describe("Managed local browser profile name created by browser_profile_import/sync. Passing this opts the Instagram tool into local authenticated Chromium for this call.")
2103
+ };
1866
2104
  var MapsPlaceIntelInputSchema = {
1867
2105
  businessName: import_zod2.z.string().min(1).describe('Business name only. If user says "Elite Roofing Denver CO", use businessName="Elite Roofing" and location="Denver, CO".'),
1868
2106
  location: import_zod2.z.string().min(1).describe('City/region/country where the business should be searched, e.g. "Denver, CO". Infer from the user request when possible.'),
@@ -2259,6 +2497,101 @@ var TranscriptChunkOutput = import_zod2.z.object({
2259
2497
  endSec: import_zod2.z.number(),
2260
2498
  text: import_zod2.z.string()
2261
2499
  });
2500
+ var InstagramBrowserOutput = import_zod2.z.object({
2501
+ mode: import_zod2.z.enum(["hosted", "local"]),
2502
+ requestedMode: import_zod2.z.enum(["auto", "hosted", "local"]),
2503
+ profileName: NullableString,
2504
+ profileSource: import_zod2.z.enum(["hosted", "managed_profile", "direct_profile_dir"]),
2505
+ profileDirConfigured: import_zod2.z.boolean(),
2506
+ executablePathConfigured: import_zod2.z.boolean()
2507
+ });
2508
+ var InstagramPaginationOutput = import_zod2.z.object({
2509
+ maxItems: import_zod2.z.number().int().min(1).max(500),
2510
+ maxScrolls: import_zod2.z.number().int().min(0).max(100),
2511
+ attemptedScrolls: import_zod2.z.number().int().min(0),
2512
+ stableScrolls: import_zod2.z.number().int().min(0),
2513
+ stableScrollLimit: import_zod2.z.number().int().min(1).max(10),
2514
+ scrollDelayMs: import_zod2.z.number().int().min(250).max(5e3),
2515
+ reachedMaxItems: import_zod2.z.boolean(),
2516
+ reachedReportedPostCount: import_zod2.z.boolean(),
2517
+ finalScrollHeight: import_zod2.z.number().int().nullable(),
2518
+ stoppedReason: import_zod2.z.enum(["max_items", "reported_post_count", "stable_scrolls", "max_scrolls", "no_scrolls"]),
2519
+ stages: import_zod2.z.array(import_zod2.z.object({
2520
+ stage: import_zod2.z.string(),
2521
+ itemCount: import_zod2.z.number().int().min(0),
2522
+ addedCount: import_zod2.z.number().int().min(0),
2523
+ scrollY: import_zod2.z.number().nullable(),
2524
+ scrollHeight: import_zod2.z.number().nullable()
2525
+ }))
2526
+ });
2527
+ var InstagramProfileContentOutputSchema = {
2528
+ handle: import_zod2.z.string(),
2529
+ profileUrl: import_zod2.z.string().url(),
2530
+ pageUrl: import_zod2.z.string().url(),
2531
+ browser: InstagramBrowserOutput,
2532
+ profileName: NullableString,
2533
+ reportedPostCount: import_zod2.z.number().int().nullable(),
2534
+ reportedPostCountText: NullableString,
2535
+ followerCountText: NullableString,
2536
+ followingCountText: NullableString,
2537
+ collectedContentCount: import_zod2.z.number().int().min(0),
2538
+ typeCounts: import_zod2.z.object({
2539
+ post: import_zod2.z.number().int().min(0),
2540
+ reel: import_zod2.z.number().int().min(0),
2541
+ tv: import_zod2.z.number().int().min(0)
2542
+ }),
2543
+ pagination: InstagramPaginationOutput,
2544
+ limited: import_zod2.z.boolean(),
2545
+ limitations: import_zod2.z.array(import_zod2.z.string()),
2546
+ items: import_zod2.z.array(import_zod2.z.object({
2547
+ url: import_zod2.z.string().url(),
2548
+ type: import_zod2.z.enum(["post", "reel", "tv"]),
2549
+ shortcode: import_zod2.z.string(),
2550
+ anchorText: NullableString,
2551
+ firstSeenStage: import_zod2.z.string()
2552
+ }))
2553
+ };
2554
+ var InstagramMediaTrackOutput = import_zod2.z.object({
2555
+ url: import_zod2.z.string().url(),
2556
+ streamType: import_zod2.z.enum(["video", "audio", "unknown"]),
2557
+ bitrate: import_zod2.z.number().int().nullable(),
2558
+ durationSec: import_zod2.z.number().nullable(),
2559
+ vencodeTag: NullableString,
2560
+ width: import_zod2.z.number().int().nullable(),
2561
+ height: import_zod2.z.number().int().nullable()
2562
+ });
2563
+ var InstagramDownloadOutput = import_zod2.z.object({
2564
+ kind: import_zod2.z.enum(["text", "image", "video", "audio", "muxed_video"]),
2565
+ url: import_zod2.z.string().url().nullable(),
2566
+ savedPath: NullableString,
2567
+ sizeBytes: import_zod2.z.number().int().nullable(),
2568
+ mimeType: NullableString,
2569
+ error: NullableString
2570
+ });
2571
+ var InstagramMediaDownloadOutputSchema = {
2572
+ sourceUrl: import_zod2.z.string().url(),
2573
+ pageUrl: import_zod2.z.string().url(),
2574
+ browser: InstagramBrowserOutput,
2575
+ type: import_zod2.z.enum(["post", "reel", "tv"]).nullable(),
2576
+ shortcode: NullableString,
2577
+ ownerName: NullableString,
2578
+ caption: NullableString,
2579
+ imageUrl: import_zod2.z.string().url().nullable(),
2580
+ trackCount: import_zod2.z.number().int().min(0),
2581
+ selectedVideoTrack: InstagramMediaTrackOutput.nullable(),
2582
+ selectedAudioTrack: InstagramMediaTrackOutput.nullable(),
2583
+ downloads: import_zod2.z.array(InstagramDownloadOutput),
2584
+ outputDir: NullableString,
2585
+ warnings: import_zod2.z.array(import_zod2.z.string()),
2586
+ limitations: import_zod2.z.array(import_zod2.z.string()),
2587
+ transcript: import_zod2.z.object({
2588
+ wordCount: import_zod2.z.number().int().min(0),
2589
+ chunkCount: import_zod2.z.number().int().min(0),
2590
+ durationMs: import_zod2.z.number().nullable(),
2591
+ transcriptText: import_zod2.z.string(),
2592
+ chunks: import_zod2.z.array(TranscriptChunkOutput)
2593
+ }).nullable()
2594
+ };
2262
2595
  var YoutubeTranscribeOutputSchema = {
2263
2596
  videoId: NullableString,
2264
2597
  url: NullableString,
@@ -2908,6 +3241,20 @@ function registerPaaExtractorMcpTools(server2, executor2, options = {}) {
2908
3241
  outputSchema: FacebookVideoTranscribeOutputSchema,
2909
3242
  annotations: liveWebToolAnnotations("Facebook Organic Video Transcription")
2910
3243
  }, async (input) => formatFacebookVideoTranscribe(await executor2.facebookVideoTranscribe(input), input));
3244
+ server2.registerTool("instagram_profile_content", {
3245
+ title: "Instagram Profile Content Discovery",
3246
+ description: withReportNote("Discover Instagram profile grid content links for a handle or profile URL. Use this when the user wants a person or brand account content inventory before selecting posts/reels to download. Returns profile stats, collected post/reel/tv URLs, shortcodes, type counts, browser mode, pagination attempts, stop reason, and limitations. For authenticated collection, first use browser_profile_import/sync, then pass browserProfile and raise maxItems/maxScrolls for longer profile pagination."),
3247
+ inputSchema: InstagramProfileContentInputSchema,
3248
+ outputSchema: InstagramProfileContentOutputSchema,
3249
+ annotations: liveWebToolAnnotations("Instagram Profile Content Discovery")
3250
+ }, async (input) => formatInstagramProfileContent(await executor2.instagramProfileContent(input), input));
3251
+ server2.registerTool("instagram_media_download", {
3252
+ title: "Instagram Post/Reel Media Download",
3253
+ description: withReportNote("Extract and download media from one Instagram post, reel, or tv URL. Use after instagram_profile_content or when the user gives a specific Instagram URL and wants the image, caption/text, reel audio/video tracks, optional muxed MP4, or optional transcript. Reels commonly expose separate video-only and audio-only MP4 tracks; this tool selects the best video and audio tracks and attempts muxing when ffmpeg is available. Pass browserProfile from browser_profile_import/sync when the media is login-gated."),
3254
+ inputSchema: InstagramMediaDownloadInputSchema,
3255
+ outputSchema: InstagramMediaDownloadOutputSchema,
3256
+ annotations: liveWebToolAnnotations("Instagram Post/Reel Media Download")
3257
+ }, async (input) => formatInstagramMediaDownload(await executor2.instagramMediaDownload(input), input));
2911
3258
  server2.registerTool("maps_place_intel", {
2912
3259
  title: "Google Maps Business Profile Details",
2913
3260
  description: withReportNote('Extract Google Maps business intelligence for one known/named business: rating, review count, category, address, phone, website, hours, booking URL, review histogram, review topics, about attributes, entity IDs, and optional review cards. Do not use this for category searches, local market prospect lists, or requests for multiple GMB/GBP profiles; use maps_search first for those. Split business name from location (e.g. "Elite Roofing Denver CO" => businessName "Elite Roofing", location "Denver, CO"). Pass includeReviews true when the user asks for reviews/customer pain.'),