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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  harvest
4
- } from "../chunk-IPW4LFOT.js";
4
+ } from "../chunk-UWSG3C5J.js";
5
5
  import "../chunk-M2S27J6Z.js";
6
6
  import {
7
7
  browserServiceApiKey
@@ -22,6 +22,9 @@ var MC_COSTS = {
22
22
  maps_review: 50,
23
23
  fb_search: 50,
24
24
  fb_transcribe: 50,
25
+ instagram_profile: 100,
26
+ instagram_media: 100,
27
+ instagram_transcribe: 50,
25
28
  browser_minute: 4e3
26
29
  };
27
30
  var MC_PER_BROWSER_MS = MC_COSTS.browser_minute / 6e4;
@@ -125,6 +128,30 @@ var CREDIT_COST_CATALOG = [
125
128
  unit: "per call",
126
129
  notes: "Whisper transcription of Facebook ad video via fal.ai."
127
130
  },
131
+ {
132
+ key: "instagram_profile",
133
+ label: "Instagram profile content discovery",
134
+ aliases: ["instagram_profile_content", "instagram profile", "instagram content list", "ig profile"],
135
+ credits: mcToCredits(MC_COSTS.instagram_profile),
136
+ unit: "per profile scan",
137
+ notes: "Browser extraction of public Instagram profile grid links. Complete history may require a logged-in profile."
138
+ },
139
+ {
140
+ key: "instagram_media",
141
+ label: "Instagram media download",
142
+ aliases: ["instagram_media_download", "instagram reel download", "instagram post download", "ig media"],
143
+ credits: mcToCredits(MC_COSTS.instagram_media),
144
+ unit: "per post or reel",
145
+ notes: "Extracts post text, image URLs, and reel audio/video tracks, with local downloads when the server can write files."
146
+ },
147
+ {
148
+ key: "instagram_transcribe",
149
+ label: "Instagram media transcription",
150
+ aliases: ["instagram transcript", "instagram reel transcript", "ig transcribe"],
151
+ credits: mcToCredits(MC_COSTS.instagram_transcribe),
152
+ unit: "per call",
153
+ notes: "Whisper transcription of selected Instagram audio/video media via fal.ai."
154
+ },
128
155
  {
129
156
  key: "browser_minute",
130
157
  label: "Interactive browser session",
@@ -212,6 +239,12 @@ var LedgerOperation = {
212
239
  FB_TRANSCRIBE: "fb_transcribe",
213
240
  FB_SEARCH_REFUND: "fb_search_refund",
214
241
  FB_TRANSCRIBE_REFUND: "fb_transcribe_refund",
242
+ INSTAGRAM_PROFILE: "instagram_profile",
243
+ INSTAGRAM_PROFILE_REFUND: "instagram_profile_refund",
244
+ INSTAGRAM_MEDIA: "instagram_media",
245
+ INSTAGRAM_MEDIA_REFUND: "instagram_media_refund",
246
+ INSTAGRAM_TRANSCRIBE: "instagram_transcribe",
247
+ INSTAGRAM_TRANSCRIBE_REFUND: "instagram_transcribe_refund",
215
248
  BROWSER_SESSION: "browser_session"
216
249
  };
217
250
 
@@ -375,4 +408,4 @@ export {
375
408
  harvestProblemResponse,
376
409
  createHarvestAttemptRecorder
377
410
  };
378
- //# sourceMappingURL=chunk-SXTXMFEQ.js.map
411
+ //# sourceMappingURL=chunk-AZ5PKU4F.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api/rates.ts","../src/api/harvest-problems.ts","../src/api/harvest-attempt-events.ts"],"sourcesContent":["export const MC_COSTS = {\n serp: 100,\n paa: 100,\n page_scrape: 100,\n url_map: 2_000,\n yt_channel: 50,\n yt_transcription: 200,\n fb_ad: 50,\n maps_search: 2_000,\n maps_place: 2_000,\n maps_review: 50,\n fb_search: 50,\n fb_transcribe: 50,\n instagram_profile: 100,\n instagram_media: 100,\n instagram_transcribe: 50,\n browser_minute: 4_000,\n} as const\n\nexport type McCostKey = keyof typeof MC_COSTS\n\nexport const MC_PER_BROWSER_MS = MC_COSTS.browser_minute / 60_000\n\nexport function browserActiveCostMc(activeMs: number): number {\n return Math.round(activeMs * MC_PER_BROWSER_MS)\n}\n\nexport const BROWSER_OPEN_MIN_BALANCE_MC = 1_000\n\nexport const MC_PER_CREDIT = 1_000\n\nexport const CREDIT_COST_CATALOG: Array<{\n key: McCostKey\n label: string\n aliases: string[]\n credits: number\n unit: string\n notes?: string\n}> = [\n {\n key: 'serp',\n label: 'SERP search',\n aliases: ['search_serp', 'serp', 'google search', 'organic results'],\n credits: mcToCredits(MC_COSTS.serp),\n unit: 'per search',\n notes: 'Returns AI Overview, PAA snippet, videos, forums, and local pack.',\n },\n {\n key: 'paa',\n label: 'PAA harvest',\n aliases: ['harvest_paa', 'paa', 'people also ask', 'questions'],\n credits: mcToCredits(MC_COSTS.paa),\n unit: 'per extracted question',\n notes: 'Includes full SERP feature extraction. Billed on actual questions returned — no cap enforced.',\n },\n {\n key: 'page_scrape',\n label: 'Page crawl / extract',\n aliases: ['extract_url', 'extract_site', 'page scrape', 'url scrape', 'single page', 'site crawl'],\n credits: mcToCredits(MC_COSTS.page_scrape),\n unit: 'per page',\n notes: 'Applies to both single-URL extraction and per-page site crawls.',\n },\n {\n key: 'url_map',\n label: 'Site URL mapping',\n aliases: ['map_site_urls', 'url map', 'site map', 'crawl urls'],\n credits: mcToCredits(MC_COSTS.url_map),\n unit: 'per mapping operation',\n notes: 'Flat rate for the full /map-urls call regardless of URL count discovered.',\n },\n {\n key: 'yt_channel',\n label: 'YouTube search / channel harvest',\n aliases: ['youtube_harvest', 'youtube search', 'youtube channel', 'yt_channel'],\n credits: mcToCredits(MC_COSTS.yt_channel),\n unit: 'per call',\n },\n {\n key: 'yt_transcription',\n label: 'YouTube transcription',\n aliases: ['youtube_transcribe', 'youtube transcript', 'transcription', 'yt_transcription'],\n credits: mcToCredits(MC_COSTS.yt_transcription),\n unit: 'per minute',\n notes: 'A 5-minute hold is taken, then reconciled to actual video duration.',\n },\n {\n key: 'fb_ad',\n label: 'Facebook search / ad lookup',\n aliases: ['facebook_page_intel', 'facebook_ad_search', 'facebook_ad', 'facebook ads', 'fb ads'],\n credits: mcToCredits(MC_COSTS.fb_ad),\n unit: 'per call',\n },\n {\n key: 'maps_search',\n label: 'Maps business search',\n aliases: ['maps_search', 'google maps search', 'gmb search', 'gbp search', 'business profiles'],\n credits: mcToCredits(MC_COSTS.maps_search),\n unit: 'per search',\n notes: 'Returns up to 50 Google Maps business/profile candidates. Use maps_place_intel to hydrate selected businesses.',\n },\n {\n key: 'maps_place',\n label: 'Maps business lookup',\n aliases: ['maps_place_intel', 'google maps', 'maps place', 'place intel'],\n credits: mcToCredits(MC_COSTS.maps_place),\n unit: 'per business',\n notes: 'Base lookup. Reviews billed separately per card at maps_review rate.',\n },\n {\n key: 'maps_review',\n label: 'Maps review',\n aliases: ['maps_reviews', 'google reviews', 'review cards', 'reviews'],\n credits: mcToCredits(MC_COSTS.maps_review),\n unit: 'per review card',\n notes: 'Charged after extraction when includeReviews is true.',\n },\n {\n key: 'fb_search',\n label: 'Facebook ad library search',\n aliases: ['facebook_search', 'fb_search', 'fb ad search'],\n credits: mcToCredits(MC_COSTS.fb_search),\n unit: 'per search',\n notes: 'Browser automation to search Facebook Ads Library by keyword.',\n },\n {\n key: 'fb_transcribe',\n label: 'Facebook ad transcription',\n aliases: ['facebook_transcribe', 'fb_transcribe', 'fb ad transcript'],\n credits: mcToCredits(MC_COSTS.fb_transcribe),\n unit: 'per call',\n notes: 'Whisper transcription of Facebook ad video via fal.ai.',\n },\n {\n key: 'instagram_profile',\n label: 'Instagram profile content discovery',\n aliases: ['instagram_profile_content', 'instagram profile', 'instagram content list', 'ig profile'],\n credits: mcToCredits(MC_COSTS.instagram_profile),\n unit: 'per profile scan',\n notes: 'Browser extraction of public Instagram profile grid links. Complete history may require a logged-in profile.',\n },\n {\n key: 'instagram_media',\n label: 'Instagram media download',\n aliases: ['instagram_media_download', 'instagram reel download', 'instagram post download', 'ig media'],\n credits: mcToCredits(MC_COSTS.instagram_media),\n unit: 'per post or reel',\n notes: 'Extracts post text, image URLs, and reel audio/video tracks, with local downloads when the server can write files.',\n },\n {\n key: 'instagram_transcribe',\n label: 'Instagram media transcription',\n aliases: ['instagram transcript', 'instagram reel transcript', 'ig transcribe'],\n credits: mcToCredits(MC_COSTS.instagram_transcribe),\n unit: 'per call',\n notes: 'Whisper transcription of selected Instagram audio/video media via fal.ai.',\n },\n {\n key: 'browser_minute',\n label: 'Interactive browser session',\n aliases: ['browser_open', 'browser agent', 'browser_agent', 'live browser', 'browse', 'browser control', 'interactive browser'],\n credits: mcToCredits(MC_COSTS.browser_minute),\n unit: 'per minute of active time',\n notes: 'Metered per second of active browser work (navigation, clicks, typing, screenshots). Idle and standby time are free. Billed against your balance as you act; close the session to stop the meter.',\n },\n]\n\nexport const CONCURRENCY_PRICE_ID = 'price_1Ta1NRS8aAcsk3TGwsRnYbix'\nexport const CONCURRENCY_SLOT_PRICE_USD = 5\nexport const CONCURRENCY_SLOT_PRICE_CURRENCY = 'usd'\nexport const CONCURRENCY_SLOT_PRICE_INTERVAL = 'month'\nexport const CONCURRENCY_SLOT_PRICE_LABEL = '$5/month'\nexport const CONCURRENCY_SLOT_TERMINAL_COMMAND = 'npx -y -p mcp-scraper@latest mcp-scraper-cli billing concurrency checkout'\n\nexport function concurrencySlotBillingInfo() {\n const billingUrl = process.env.CONCURRENCY_BILLING_URL ?? 'https://mcpscraper.dev/billing'\n return {\n product: 'Extra concurrency slot',\n price_label: CONCURRENCY_SLOT_PRICE_LABEL,\n unit_amount_usd: CONCURRENCY_SLOT_PRICE_USD,\n currency: CONCURRENCY_SLOT_PRICE_CURRENCY,\n interval: CONCURRENCY_SLOT_PRICE_INTERVAL,\n billing_url: billingUrl,\n terminal_command: CONCURRENCY_SLOT_TERMINAL_COMMAND,\n terminal_command_with_api_key_env: `MCP_SCRAPER_API_KEY=sk_live_your_key ${CONCURRENCY_SLOT_TERMINAL_COMMAND}`,\n }\n}\n\nexport const FREE_SIGNUP_MC = 500_000\nexport const FREE_MONTHLY_REFRESH_MC = 250_000\n\nexport const BALANCE_PRICE_IDS: Record<string, number> = {\n 'price_1TZx6rS8aAcsk3TGNMc1Vgpo': 11_000_000,\n 'price_1TZx6sS8aAcsk3TGxgqB7khO': 27_500_000,\n 'price_1TZx6tS8aAcsk3TG8PnJqHlG': 60_500_000,\n 'price_1TZx6tS8aAcsk3TGNgRMpy0e': 121_000_000,\n}\n\nexport const BALANCE_PACK_LABELS: Record<string, string> = {\n 'price_1TZx6rS8aAcsk3TGNMc1Vgpo': '$10',\n 'price_1TZx6sS8aAcsk3TGxgqB7khO': '$25',\n 'price_1TZx6tS8aAcsk3TG8PnJqHlG': '$50',\n 'price_1TZx6tS8aAcsk3TGNgRMpy0e': '$100',\n}\n\nexport function mcToCredits(mc: number): number {\n return mc / MC_PER_CREDIT\n}\n\nexport function insufficientBalanceResponse(balanceMc: number, requiredMc: number) {\n const topupUrl = process.env.TOPUP_URL ?? 'https://mcpscraper.dev/billing'\n const balanceCredits = mcToCredits(balanceMc)\n const requiredCredits = mcToCredits(requiredMc)\n return {\n error: 'insufficient_balance',\n error_code: 'insufficient_balance' as const,\n message: `Insufficient credits. Balance: ${balanceCredits} credits. This call requires ${requiredCredits} credits. Top up at ${topupUrl}`,\n balance_credits: balanceCredits,\n required_credits: requiredCredits,\n topup_url: topupUrl,\n }\n}\n\nexport const LedgerOperation = {\n TOPUP: 'topup',\n SIGNUP_GRANT: 'signup_grant',\n MONTHLY_REFRESH: 'monthly_free_refresh',\n PAA: 'paa',\n PAA_REFUND: 'paa_refund',\n SERP: 'serp',\n REFUND: 'refund',\n TRANSCRIPTION: 'transcription',\n TRANSCRIPTION_HOLD: 'transcription_hold',\n TRANSCRIPTION_REFUND: 'transcription_refund',\n YT_CHANNEL: 'yt_channel',\n FB_AD: 'fb_ad',\n MAPS_SEARCH: 'maps_search',\n MAPS_PLACE: 'maps_place',\n MAPS_REVIEW: 'maps_review',\n MAPS_REVIEW_REFUND: 'maps_review_refund',\n EXTRACT_SITE: 'extract_site',\n EXTRACT_SITE_REFUND: 'extract_site_refund',\n EXTRACT_URL: 'page_scrape',\n URL_MAP: 'url_map',\n EXTRACT_SITE_HOLD: 'extract_site_hold',\n YT_CHANNEL_REFUND: 'yt_channel_refund',\n FB_AD_REFUND: 'fb_ad_refund',\n URL_MAP_REFUND: 'url_map_refund',\n FB_SEARCH: 'fb_search',\n FB_TRANSCRIBE: 'fb_transcribe',\n FB_SEARCH_REFUND: 'fb_search_refund',\n FB_TRANSCRIBE_REFUND: 'fb_transcribe_refund',\n INSTAGRAM_PROFILE: 'instagram_profile',\n INSTAGRAM_PROFILE_REFUND: 'instagram_profile_refund',\n INSTAGRAM_MEDIA: 'instagram_media',\n INSTAGRAM_MEDIA_REFUND:'instagram_media_refund',\n INSTAGRAM_TRANSCRIBE: 'instagram_transcribe',\n INSTAGRAM_TRANSCRIBE_REFUND: 'instagram_transcribe_refund',\n BROWSER_SESSION: 'browser_session',\n} as const\n\nexport type LedgerOperation = typeof LedgerOperation[keyof typeof LedgerOperation]\n","import { CaptchaError, LocationMismatchError, RequestAbortedError } from '../errors.js'\n\nexport type HarvestProblemCode =\n | 'request_aborted'\n | 'captcha_exhausted'\n | 'location_mismatch'\n | 'proxy_tunnel_failed'\n | 'proxy_unavailable'\n | 'harvest_timeout'\n | 'extraction_failed'\n\nexport interface HarvestProblem {\n error_code: HarvestProblemCode\n error_type: string\n message: string\n retryable: boolean\n httpStatus: number\n terminalStatus: 'cancelled' | 'failed'\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\nfunction looksLikeTimeout(err: unknown, message: string): boolean {\n if (err instanceof DOMException && (err.name === 'TimeoutError' || err.name === 'AbortError')) return true\n return /timeout|timed out|Timeout \\d+ms exceeded|deadline/i.test(message)\n}\n\nfunction looksLikeCaptcha(message: string): boolean {\n return /captcha|recaptcha|unusual traffic|google\\.com\\/sorry|blocked/i.test(message)\n}\n\nfunction looksLikeProxyTunnelFailure(message: string): boolean {\n return /ERR_TUNNEL_CONNECTION_FAILED|ERR_PROXY_CONNECTION_FAILED|ERR_SOCKS_CONNECTION_FAILED|tunnel connection failed|proxy connection failed|transport error: proxy/i.test(message)\n}\n\nfunction looksLikeProxyUnavailable(message: string): boolean {\n return /proxy unavailable|proxy_unavailable|connection_test_failed|did not return a proxy id|configured fallback/i.test(message)\n}\n\nexport function classifyHarvestProblem(err: unknown): HarvestProblem {\n const message = errorMessage(err)\n\n if (err instanceof RequestAbortedError) {\n return {\n error_code: 'request_aborted',\n error_type: 'request_aborted',\n message,\n retryable: true,\n httpStatus: 408,\n terminalStatus: 'cancelled',\n }\n }\n\n if (err instanceof CaptchaError || looksLikeCaptcha(message)) {\n return {\n error_code: 'captcha_exhausted',\n error_type: 'captcha',\n message,\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (err instanceof LocationMismatchError) {\n return {\n error_code: 'location_mismatch',\n error_type: 'location_mismatch',\n message,\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (looksLikeProxyTunnelFailure(message)) {\n return {\n error_code: 'proxy_tunnel_failed',\n error_type: 'proxy_tunnel_failed',\n message,\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (looksLikeProxyUnavailable(message)) {\n return {\n error_code: 'proxy_unavailable',\n error_type: 'proxy_unavailable',\n message,\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (looksLikeTimeout(err, message)) {\n return {\n error_code: 'harvest_timeout',\n error_type: 'timeout',\n message,\n retryable: true,\n httpStatus: 504,\n terminalStatus: 'failed',\n }\n }\n\n return {\n error_code: 'extraction_failed',\n error_type: 'extraction',\n message,\n retryable: false,\n httpStatus: 500,\n terminalStatus: 'failed',\n }\n}\n\nexport function serializeHarvestProblem(problem: HarvestProblem): string {\n return JSON.stringify({\n error_code: problem.error_code,\n error_type: problem.error_type,\n message: problem.message,\n retryable: problem.retryable,\n })\n}\n\nexport function harvestProblemResponse(problem: HarvestProblem): {\n error: string\n error_code: HarvestProblemCode\n error_type: string\n retryable: boolean\n} {\n return {\n error: problem.message,\n error_code: problem.error_code,\n error_type: problem.error_type,\n retryable: problem.retryable,\n }\n}\n","import type { HarvestAttemptLogEvent } from '../harvest.js'\nimport { finishHarvestAttempt, startHarvestAttempt } from './db.js'\n\nexport function createHarvestAttemptRecorder(jobId: string, userId: number | bigint) {\n return async (event: HarvestAttemptLogEvent): Promise<void> => {\n if (event.type === 'started') {\n await startHarvestAttempt({\n jobId,\n userId,\n attemptNumber: event.attemptNumber,\n maxAttempts: event.maxAttempts,\n query: event.query,\n location: event.location,\n maxQuestions: event.maxQuestions,\n startedAt: event.startedAt,\n })\n return\n }\n\n await finishHarvestAttempt({\n jobId,\n attemptNumber: event.attemptNumber,\n outcome: event.outcome,\n kernelSessionId: event.kernelSessionId,\n questionCount: event.questionCount,\n durationMs: event.durationMs,\n error: event.error,\n willRetry: event.willRetry,\n kernelDeleteStarted: event.cleanup.kernelDeleteStarted,\n kernelDeleteSucceeded: event.cleanup.kernelDeleteSucceeded,\n kernelDeleteError: event.cleanup.kernelDeleteError,\n browserCloseSucceeded: event.cleanup.browserCloseSucceeded,\n browserCloseError: event.cleanup.browserCloseError,\n debug: event.debug,\n completedAt: event.completedAt,\n })\n }\n}\n"],"mappings":"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","names":[]}
@@ -50,11 +50,11 @@ function renderInstallTerminal(options) {
50
50
  "1/1 install surfaces ready",
51
51
  colorize("Newest: local Chrome profile mode for browser_open, browser_profile_import/sync, and one-server MCP installs.", "lime", color),
52
52
  "",
53
- `${colorize("Tools", "cyan", color)} ${colorize("(43 MCP tools)", "muted", color)}`,
53
+ `${colorize("Tools", "cyan", color)} ${colorize("(46 MCP tools)", "muted", color)}`,
54
54
  toolRow("search", ["harvest_paa", "search_serp", "maps_search", "maps_place_intel"], color),
55
55
  toolRow("extract", ["extract_url", "map_site_urls", "extract_site", "directory_workflow"], color),
56
56
  toolRow("build", ["rank_tracker_blueprint", "cron plan", "database prompt"], color),
57
- toolRow("media", ["youtube_harvest", "youtube_transcribe", "facebook_ad_search", "facebook_page_intel", "facebook_ad_transcribe", "facebook_video_transcribe"], color),
57
+ 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),
58
58
  toolRow("browser", ["browser_open", "browser_profile_import", "browser_profile_sync", "browser_screenshot", "browser_read", "browser_locate", "browser_replay_mark", "browser_replay_annotate"], color),
59
59
  toolRow("account", ["credits_info", "local reports", "MCP resources"], color),
60
60
  `${colorize("Workflows", "cyan", color)} ${colorize("(MCP + CLI + API)", "muted", color)}`,
@@ -100,4 +100,4 @@ function renderInstallTerminal(options) {
100
100
  export {
101
101
  renderInstallTerminal
102
102
  };
103
- //# sourceMappingURL=chunk-Q4STSM63.js.map
103
+ //# sourceMappingURL=chunk-C3FGVJWH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/install-terminal.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"],"mappings":"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","names":[]}
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-LFATOGDF.js";
8
8
  import {
9
9
  PACKAGE_VERSION
10
- } from "./chunk-D4JDGKOV.js";
10
+ } from "./chunk-PVXDEREW.js";
11
11
 
12
12
  // src/harvest-timeout.ts
13
13
  var VERCEL_FUNCTION_MAX_MS = 3e5;
@@ -506,7 +506,9 @@ ${headingSection}${kpoSection}${brandingSection}${bodySection}${screenshotSectio
506
506
  entityTypes: kpo?.type ?? [],
507
507
  napScore: kpo?.napScore ?? null,
508
508
  missingSchemaFields: kpo?.missingFields ?? [],
509
- screenshotSaved: screenshotPath ?? null
509
+ screenshotSaved: screenshotPath ?? null,
510
+ branding: branding ?? null,
511
+ mediaAssets: media?.assets ?? null
510
512
  };
511
513
  if (screenshotMeta?.base64) {
512
514
  return {
@@ -1478,6 +1480,216 @@ ${chunkRows}` : "",
1478
1480
  }
1479
1481
  };
1480
1482
  }
1483
+ function structuredInstagramBrowser(raw) {
1484
+ const browser = raw && typeof raw === "object" ? raw : {};
1485
+ const mode = browser.mode === "local" ? "local" : "hosted";
1486
+ const requestedMode = browser.requestedMode === "local" || browser.requestedMode === "hosted" || browser.requestedMode === "auto" ? browser.requestedMode : "auto";
1487
+ const profileSource = browser.profileSource === "managed_profile" || browser.profileSource === "direct_profile_dir" ? browser.profileSource : "hosted";
1488
+ return {
1489
+ mode,
1490
+ requestedMode,
1491
+ profileName: typeof browser.profileName === "string" ? browser.profileName : null,
1492
+ profileSource,
1493
+ profileDirConfigured: browser.profileDirConfigured === true,
1494
+ executablePathConfigured: browser.executablePathConfigured === true
1495
+ };
1496
+ }
1497
+ function structuredInstagramPagination(raw, input) {
1498
+ const pagination = raw && typeof raw === "object" ? raw : {};
1499
+ const stages = Array.isArray(pagination.stages) ? pagination.stages : [];
1500
+ const stoppedReason = typeof pagination.stoppedReason === "string" && ["max_items", "reported_post_count", "stable_scrolls", "max_scrolls", "no_scrolls"].includes(pagination.stoppedReason) ? pagination.stoppedReason : "no_scrolls";
1501
+ return {
1502
+ maxItems: typeof pagination.maxItems === "number" ? pagination.maxItems : Number(input.maxItems ?? 50),
1503
+ maxScrolls: typeof pagination.maxScrolls === "number" ? pagination.maxScrolls : Number(input.maxScrolls ?? 10),
1504
+ attemptedScrolls: typeof pagination.attemptedScrolls === "number" ? pagination.attemptedScrolls : 0,
1505
+ stableScrolls: typeof pagination.stableScrolls === "number" ? pagination.stableScrolls : 0,
1506
+ stableScrollLimit: typeof pagination.stableScrollLimit === "number" ? pagination.stableScrollLimit : Number(input.stableScrollLimit ?? 4),
1507
+ scrollDelayMs: typeof pagination.scrollDelayMs === "number" ? pagination.scrollDelayMs : Number(input.scrollDelayMs ?? 1200),
1508
+ reachedMaxItems: pagination.reachedMaxItems === true,
1509
+ reachedReportedPostCount: pagination.reachedReportedPostCount === true,
1510
+ finalScrollHeight: typeof pagination.finalScrollHeight === "number" ? pagination.finalScrollHeight : null,
1511
+ stoppedReason,
1512
+ stages: stages.map((stage) => {
1513
+ const row = stage && typeof stage === "object" ? stage : {};
1514
+ return {
1515
+ stage: String(row.stage ?? ""),
1516
+ itemCount: typeof row.itemCount === "number" ? row.itemCount : 0,
1517
+ addedCount: typeof row.addedCount === "number" ? row.addedCount : 0,
1518
+ scrollY: typeof row.scrollY === "number" ? row.scrollY : null,
1519
+ scrollHeight: typeof row.scrollHeight === "number" ? row.scrollHeight : null
1520
+ };
1521
+ })
1522
+ };
1523
+ }
1524
+ function formatInstagramProfileContent(raw, input) {
1525
+ const parsed = parseData(raw);
1526
+ if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
1527
+ const d = parsed.data;
1528
+ const items = Array.isArray(d.items) ? d.items : [];
1529
+ const typeCounts = d.typeCounts;
1530
+ const limitations = Array.isArray(d.limitations) ? d.limitations.map(String) : [];
1531
+ const browser = structuredInstagramBrowser(d.browser);
1532
+ const pagination = structuredInstagramPagination(d.pagination, input);
1533
+ const itemRows = items.slice(0, 100).map(
1534
+ (item, i) => `| ${i + 1} | ${item.type} | \`${item.shortcode}\` | ${item.url} | ${cell(item.firstSeenStage ?? "")} |`
1535
+ ).join("\n");
1536
+ const browserLabel = browser.mode === "local" ? `local profile ${browser.profileName ? `\`${browser.profileName}\`` : "(unnamed)"}` : "hosted browser";
1537
+ const full = [
1538
+ `# Instagram Profile Content: ${d.handle ?? input.handle ?? input.url ?? "profile"}`,
1539
+ `**Collected:** ${items.length} items \xB7 posts ${typeCounts?.post ?? 0} \xB7 reels ${typeCounts?.reel ?? 0} \xB7 tv ${typeCounts?.tv ?? 0}`,
1540
+ `**Browser:** ${browserLabel}`,
1541
+ `**Pagination:** ${pagination.attemptedScrolls} scrolls \xB7 stopped: ${pagination.stoppedReason}`,
1542
+ d.reportedPostCountText ? `**Profile count:** ${d.reportedPostCountText}` : "",
1543
+ d.followerCountText ? `**Followers:** ${d.followerCountText}` : "",
1544
+ `
1545
+ ## Content Links
1546
+ | # | Type | Shortcode | URL | First Seen |
1547
+ |---|------|-----------|-----|------------|
1548
+ ${itemRows || "| \u2014 | \u2014 | \u2014 | \u2014 | \u2014 |"}`,
1549
+ limitations.length ? `
1550
+ ## Limits
1551
+ ${limitations.map((l) => `- ${l}`).join("\n")}` : "",
1552
+ `
1553
+ ---
1554
+ \u{1F4A1} Use \`instagram_media_download\` with one of these URLs to download text, image, reel tracks, and optional transcript.`
1555
+ ].filter(Boolean).join("\n");
1556
+ return {
1557
+ ...oneBlock(full),
1558
+ structuredContent: {
1559
+ handle: String(d.handle ?? input.handle ?? ""),
1560
+ profileUrl: String(d.profileUrl ?? input.url ?? ""),
1561
+ pageUrl: String(d.pageUrl ?? d.profileUrl ?? input.url ?? ""),
1562
+ browser,
1563
+ profileName: typeof d.profileName === "string" ? d.profileName : null,
1564
+ reportedPostCount: typeof d.reportedPostCount === "number" ? d.reportedPostCount : null,
1565
+ reportedPostCountText: typeof d.reportedPostCountText === "string" ? d.reportedPostCountText : null,
1566
+ followerCountText: typeof d.followerCountText === "string" ? d.followerCountText : null,
1567
+ followingCountText: typeof d.followingCountText === "string" ? d.followingCountText : null,
1568
+ collectedContentCount: items.length,
1569
+ typeCounts: {
1570
+ post: Number(typeCounts?.post ?? 0),
1571
+ reel: Number(typeCounts?.reel ?? 0),
1572
+ tv: Number(typeCounts?.tv ?? 0)
1573
+ },
1574
+ pagination,
1575
+ limited: d.limited === true,
1576
+ limitations,
1577
+ items: items.map((item) => ({
1578
+ url: String(item.url ?? ""),
1579
+ type: item.type,
1580
+ shortcode: String(item.shortcode ?? ""),
1581
+ anchorText: item.anchorText ?? null,
1582
+ firstSeenStage: String(item.firstSeenStage ?? "")
1583
+ }))
1584
+ }
1585
+ };
1586
+ }
1587
+ function structuredInstagramTrack(track) {
1588
+ if (!track) return null;
1589
+ return {
1590
+ url: String(track.url ?? ""),
1591
+ streamType: track.streamType ?? "unknown",
1592
+ bitrate: typeof track.bitrate === "number" ? track.bitrate : null,
1593
+ durationSec: typeof track.durationSec === "number" ? track.durationSec : null,
1594
+ vencodeTag: track.vencodeTag ?? null,
1595
+ width: typeof track.width === "number" ? track.width : null,
1596
+ height: typeof track.height === "number" ? track.height : null
1597
+ };
1598
+ }
1599
+ function formatInstagramMediaDownload(raw, input) {
1600
+ const parsed = parseData(raw);
1601
+ if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
1602
+ const d = parsed.data;
1603
+ const tracks = Array.isArray(d.tracks) ? d.tracks : [];
1604
+ const downloads = Array.isArray(d.downloads) ? d.downloads : [];
1605
+ const warnings = Array.isArray(d.warnings) ? d.warnings.map(String) : [];
1606
+ const limitations = Array.isArray(d.limitations) ? d.limitations.map(String) : [];
1607
+ const transcript = d.transcript;
1608
+ const transcriptText = transcript?.text ?? "";
1609
+ const chunks = transcript?.chunks ?? [];
1610
+ const browser = structuredInstagramBrowser(d.browser);
1611
+ const browserLabel = browser.mode === "local" ? `local profile ${browser.profileName ? `\`${browser.profileName}\`` : "(unnamed)"}` : "hosted browser";
1612
+ const downloadRows = downloads.map((download, i) => {
1613
+ const status = download.error ? `error: ${cell(download.error)}` : `${download.sizeBytes ?? 0} bytes`;
1614
+ return `| ${i + 1} | ${download.kind} | ${download.savedPath ? `\`${download.savedPath}\`` : "\u2014"} | ${status} |`;
1615
+ }).join("\n");
1616
+ const trackRows = tracks.slice(0, 20).map(
1617
+ (track, i) => `| ${i + 1} | ${track.streamType} | ${track.bitrate ?? "\u2014"} | ${track.durationSec ?? "\u2014"} | ${cell(track.vencodeTag ?? "")} |`
1618
+ ).join("\n");
1619
+ const full = [
1620
+ `# Instagram Media Download`,
1621
+ `**URL:** ${d.pageUrl ?? input.url}`,
1622
+ `**Browser:** ${browserLabel}`,
1623
+ d.ownerName ? `**Owner:** ${d.ownerName}` : "",
1624
+ d.shortcode ? `**Shortcode:** \`${d.shortcode}\`` : "",
1625
+ d.caption ? `
1626
+ ## Caption
1627
+ ${truncate(String(d.caption), 1200)}` : "",
1628
+ d.imageUrl ? `
1629
+ ## Image
1630
+ ${d.imageUrl}` : "",
1631
+ tracks.length ? `
1632
+ ## Media Tracks
1633
+ | # | Type | Bitrate | Duration | Tag |
1634
+ |---|------|---------|----------|-----|
1635
+ ${trackRows}` : "\n## Media Tracks\n*None captured.*",
1636
+ downloads.length ? `
1637
+ ## Downloads
1638
+ | # | Kind | File | Status |
1639
+ |---|------|------|--------|
1640
+ ${downloadRows}` : "",
1641
+ d.outputDir ? `
1642
+ **Output directory:** \`${d.outputDir}\`` : "",
1643
+ transcript ? `
1644
+ ## Transcript
1645
+ **${wordCount(transcriptText)} words** \xB7 ${chunks.length} chunks
1646
+
1647
+ ${transcriptText}` : "",
1648
+ warnings.length ? `
1649
+ ## Warnings
1650
+ ${warnings.map((w) => `- ${w}`).join("\n")}` : "",
1651
+ limitations.length ? `
1652
+ ## Limits
1653
+ ${limitations.map((l) => `- ${l}`).join("\n")}` : "",
1654
+ `
1655
+ ---
1656
+ \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.`
1657
+ ].filter(Boolean).join("\n");
1658
+ return {
1659
+ ...oneBlock(full),
1660
+ structuredContent: {
1661
+ sourceUrl: String(d.sourceUrl ?? input.url),
1662
+ pageUrl: String(d.pageUrl ?? input.url),
1663
+ browser,
1664
+ type: d.type === "post" || d.type === "reel" || d.type === "tv" ? d.type : null,
1665
+ shortcode: typeof d.shortcode === "string" ? d.shortcode : null,
1666
+ ownerName: typeof d.ownerName === "string" ? d.ownerName : null,
1667
+ caption: typeof d.caption === "string" ? d.caption : null,
1668
+ imageUrl: typeof d.imageUrl === "string" ? d.imageUrl : null,
1669
+ trackCount: tracks.length,
1670
+ selectedVideoTrack: structuredInstagramTrack(d.selectedVideoTrack),
1671
+ selectedAudioTrack: structuredInstagramTrack(d.selectedAudioTrack),
1672
+ downloads: downloads.map((download) => ({
1673
+ kind: download.kind,
1674
+ url: download.url ?? null,
1675
+ savedPath: download.savedPath ?? null,
1676
+ sizeBytes: typeof download.sizeBytes === "number" ? download.sizeBytes : null,
1677
+ mimeType: download.mimeType ?? null,
1678
+ error: download.error ?? null
1679
+ })),
1680
+ outputDir: typeof d.outputDir === "string" ? d.outputDir : null,
1681
+ warnings,
1682
+ limitations,
1683
+ transcript: transcript ? {
1684
+ wordCount: wordCount(transcriptText),
1685
+ chunkCount: chunks.length,
1686
+ durationMs: typeof transcript.durationMs === "number" ? transcript.durationMs : null,
1687
+ transcriptText,
1688
+ chunks: structuredTranscriptChunks(chunks)
1689
+ } : null
1690
+ }
1691
+ };
1692
+ }
1481
1693
  function formatCaptureSerpSnapshot(raw, input) {
1482
1694
  const parsed = parseData(raw);
1483
1695
  if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
@@ -1628,6 +1840,26 @@ var FacebookVideoTranscribeInputSchema = {
1628
1840
  url: 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."),
1629
1841
  quality: 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.")
1630
1842
  };
1843
+ var InstagramProfileContentInputSchema = {
1844
+ handle: 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."),
1845
+ url: z.string().url().optional().describe("Instagram profile URL, e.g. https://www.instagram.com/nasaartemis/. Provide handle or url."),
1846
+ maxItems: 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."),
1847
+ maxScrolls: 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."),
1848
+ scrollDelayMs: 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."),
1849
+ stableScrollLimit: 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."),
1850
+ browserMode: 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."),
1851
+ browserProfile: 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.")
1852
+ };
1853
+ var InstagramMediaDownloadInputSchema = {
1854
+ url: 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."),
1855
+ mediaTypes: z.array(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."),
1856
+ downloadMedia: 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."),
1857
+ downloadAllTracks: 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."),
1858
+ includeTranscript: z.boolean().default(false).describe("Transcribe the selected audio track when available. This adds transcription cost and may take longer."),
1859
+ mux: 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."),
1860
+ browserMode: 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."),
1861
+ browserProfile: 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.")
1862
+ };
1631
1863
  var MapsPlaceIntelInputSchema = {
1632
1864
  businessName: z.string().min(1).describe('Business name only. If user says "Elite Roofing Denver CO", use businessName="Elite Roofing" and location="Denver, CO".'),
1633
1865
  location: 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.'),
@@ -2024,6 +2256,101 @@ var TranscriptChunkOutput = z.object({
2024
2256
  endSec: z.number(),
2025
2257
  text: z.string()
2026
2258
  });
2259
+ var InstagramBrowserOutput = z.object({
2260
+ mode: z.enum(["hosted", "local"]),
2261
+ requestedMode: z.enum(["auto", "hosted", "local"]),
2262
+ profileName: NullableString,
2263
+ profileSource: z.enum(["hosted", "managed_profile", "direct_profile_dir"]),
2264
+ profileDirConfigured: z.boolean(),
2265
+ executablePathConfigured: z.boolean()
2266
+ });
2267
+ var InstagramPaginationOutput = z.object({
2268
+ maxItems: z.number().int().min(1).max(500),
2269
+ maxScrolls: z.number().int().min(0).max(100),
2270
+ attemptedScrolls: z.number().int().min(0),
2271
+ stableScrolls: z.number().int().min(0),
2272
+ stableScrollLimit: z.number().int().min(1).max(10),
2273
+ scrollDelayMs: z.number().int().min(250).max(5e3),
2274
+ reachedMaxItems: z.boolean(),
2275
+ reachedReportedPostCount: z.boolean(),
2276
+ finalScrollHeight: z.number().int().nullable(),
2277
+ stoppedReason: z.enum(["max_items", "reported_post_count", "stable_scrolls", "max_scrolls", "no_scrolls"]),
2278
+ stages: z.array(z.object({
2279
+ stage: z.string(),
2280
+ itemCount: z.number().int().min(0),
2281
+ addedCount: z.number().int().min(0),
2282
+ scrollY: z.number().nullable(),
2283
+ scrollHeight: z.number().nullable()
2284
+ }))
2285
+ });
2286
+ var InstagramProfileContentOutputSchema = {
2287
+ handle: z.string(),
2288
+ profileUrl: z.string().url(),
2289
+ pageUrl: z.string().url(),
2290
+ browser: InstagramBrowserOutput,
2291
+ profileName: NullableString,
2292
+ reportedPostCount: z.number().int().nullable(),
2293
+ reportedPostCountText: NullableString,
2294
+ followerCountText: NullableString,
2295
+ followingCountText: NullableString,
2296
+ collectedContentCount: z.number().int().min(0),
2297
+ typeCounts: z.object({
2298
+ post: z.number().int().min(0),
2299
+ reel: z.number().int().min(0),
2300
+ tv: z.number().int().min(0)
2301
+ }),
2302
+ pagination: InstagramPaginationOutput,
2303
+ limited: z.boolean(),
2304
+ limitations: z.array(z.string()),
2305
+ items: z.array(z.object({
2306
+ url: z.string().url(),
2307
+ type: z.enum(["post", "reel", "tv"]),
2308
+ shortcode: z.string(),
2309
+ anchorText: NullableString,
2310
+ firstSeenStage: z.string()
2311
+ }))
2312
+ };
2313
+ var InstagramMediaTrackOutput = z.object({
2314
+ url: z.string().url(),
2315
+ streamType: z.enum(["video", "audio", "unknown"]),
2316
+ bitrate: z.number().int().nullable(),
2317
+ durationSec: z.number().nullable(),
2318
+ vencodeTag: NullableString,
2319
+ width: z.number().int().nullable(),
2320
+ height: z.number().int().nullable()
2321
+ });
2322
+ var InstagramDownloadOutput = z.object({
2323
+ kind: z.enum(["text", "image", "video", "audio", "muxed_video"]),
2324
+ url: z.string().url().nullable(),
2325
+ savedPath: NullableString,
2326
+ sizeBytes: z.number().int().nullable(),
2327
+ mimeType: NullableString,
2328
+ error: NullableString
2329
+ });
2330
+ var InstagramMediaDownloadOutputSchema = {
2331
+ sourceUrl: z.string().url(),
2332
+ pageUrl: z.string().url(),
2333
+ browser: InstagramBrowserOutput,
2334
+ type: z.enum(["post", "reel", "tv"]).nullable(),
2335
+ shortcode: NullableString,
2336
+ ownerName: NullableString,
2337
+ caption: NullableString,
2338
+ imageUrl: z.string().url().nullable(),
2339
+ trackCount: z.number().int().min(0),
2340
+ selectedVideoTrack: InstagramMediaTrackOutput.nullable(),
2341
+ selectedAudioTrack: InstagramMediaTrackOutput.nullable(),
2342
+ downloads: z.array(InstagramDownloadOutput),
2343
+ outputDir: NullableString,
2344
+ warnings: z.array(z.string()),
2345
+ limitations: z.array(z.string()),
2346
+ transcript: z.object({
2347
+ wordCount: z.number().int().min(0),
2348
+ chunkCount: z.number().int().min(0),
2349
+ durationMs: z.number().nullable(),
2350
+ transcriptText: z.string(),
2351
+ chunks: z.array(TranscriptChunkOutput)
2352
+ }).nullable()
2353
+ };
2027
2354
  var YoutubeTranscribeOutputSchema = {
2028
2355
  videoId: NullableString,
2029
2356
  url: NullableString,
@@ -2673,6 +3000,20 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
2673
3000
  outputSchema: FacebookVideoTranscribeOutputSchema,
2674
3001
  annotations: liveWebToolAnnotations("Facebook Organic Video Transcription")
2675
3002
  }, async (input) => formatFacebookVideoTranscribe(await executor.facebookVideoTranscribe(input), input));
3003
+ server.registerTool("instagram_profile_content", {
3004
+ title: "Instagram Profile Content Discovery",
3005
+ 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."),
3006
+ inputSchema: InstagramProfileContentInputSchema,
3007
+ outputSchema: InstagramProfileContentOutputSchema,
3008
+ annotations: liveWebToolAnnotations("Instagram Profile Content Discovery")
3009
+ }, async (input) => formatInstagramProfileContent(await executor.instagramProfileContent(input), input));
3010
+ server.registerTool("instagram_media_download", {
3011
+ title: "Instagram Post/Reel Media Download",
3012
+ 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."),
3013
+ inputSchema: InstagramMediaDownloadInputSchema,
3014
+ outputSchema: InstagramMediaDownloadOutputSchema,
3015
+ annotations: liveWebToolAnnotations("Instagram Post/Reel Media Download")
3016
+ }, async (input) => formatInstagramMediaDownload(await executor.instagramMediaDownload(input), input));
2676
3017
  server.registerTool("maps_place_intel", {
2677
3018
  title: "Google Maps Business Profile Details",
2678
3019
  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.'),
@@ -2933,6 +3274,12 @@ var HttpMcpToolExecutor = class {
2933
3274
  facebookVideoTranscribe(input) {
2934
3275
  return this.call("/facebook/video-transcribe", input, this.httpTimeoutOverrideMs ?? 24e4);
2935
3276
  }
3277
+ instagramProfileContent(input) {
3278
+ return this.call("/instagram/profile-content", input, this.httpTimeoutOverrideMs ?? 24e4);
3279
+ }
3280
+ instagramMediaDownload(input) {
3281
+ return this.call("/instagram/media-download", input, this.httpTimeoutOverrideMs ?? 3e5);
3282
+ }
2936
3283
  mapsPlaceIntel(input) {
2937
3284
  return this.call("/maps/place", input);
2938
3285
  }
@@ -2995,4 +3342,4 @@ export {
2995
3342
  registerPaaExtractorMcpTools,
2996
3343
  HttpMcpToolExecutor
2997
3344
  };
2998
- //# sourceMappingURL=chunk-55T4SRLJ.js.map
3345
+ //# sourceMappingURL=chunk-MGWGZBL5.js.map