mcp-scraper 0.33.6 → 0.34.0

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 (64) hide show
  1. package/README.md +2 -2
  2. package/dist/bin/api-server.cjs +13558 -12516
  3. package/dist/bin/api-server.cjs.map +1 -1
  4. package/dist/bin/api-server.js +3 -3
  5. package/dist/bin/mcp-scraper-cli.cjs +13 -2
  6. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  7. package/dist/bin/mcp-scraper-cli.js +14 -4
  8. package/dist/bin/mcp-scraper-cli.js.map +1 -1
  9. package/dist/bin/mcp-scraper-install.cjs +2 -2
  10. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  11. package/dist/bin/mcp-scraper-install.js +2 -2
  12. package/dist/bin/mcp-stdio-server.cjs +117 -7
  13. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  14. package/dist/bin/mcp-stdio-server.js +7 -7
  15. package/dist/bin/paa-harvest.cjs +128 -13
  16. package/dist/bin/paa-harvest.cjs.map +1 -1
  17. package/dist/bin/paa-harvest.js +4 -4
  18. package/dist/{chunk-DUS4ENOY.js → chunk-2EDFOQD7.js} +2 -2
  19. package/dist/{chunk-DUS4ENOY.js.map → chunk-2EDFOQD7.js.map} +1 -1
  20. package/dist/{chunk-C2OD4ELT.js → chunk-3HBPKR5G.js} +2 -2
  21. package/dist/{chunk-A54ELKJK.js → chunk-4KQVIYIH.js} +139 -17
  22. package/dist/chunk-4KQVIYIH.js.map +1 -0
  23. package/dist/{chunk-AIK7WQD5.js → chunk-62DQAWPF.js} +141 -3
  24. package/dist/chunk-62DQAWPF.js.map +1 -0
  25. package/dist/{chunk-V73MPRU6.js → chunk-CB5C3BPB.js} +17 -1
  26. package/dist/chunk-CB5C3BPB.js.map +1 -0
  27. package/dist/{chunk-6ZUK4H66.js → chunk-J32XSMJI.js} +117 -8
  28. package/dist/chunk-J32XSMJI.js.map +1 -0
  29. package/dist/{chunk-LKIP7VDN.js → chunk-JBSGBSGT.js} +46 -3
  30. package/dist/chunk-JBSGBSGT.js.map +1 -0
  31. package/dist/chunk-NFMIHEYE.js +7 -0
  32. package/dist/chunk-NFMIHEYE.js.map +1 -0
  33. package/dist/{chunk-XL6YIWJ3.js → chunk-SUOHUXQS.js} +3 -3
  34. package/dist/{chunk-WJJTGRK4.js → chunk-V36LS5YV.js} +2 -2
  35. package/dist/{chunk-AT2PFWHW.js → chunk-ZID3WQID.js} +2 -2
  36. package/dist/{db-5SZQUI7A.js → db-YAI5AQOI.js} +16 -2
  37. package/dist/{extract-bundle-YNXTFCC3.js → extract-bundle-GPZRLBVY.js} +2 -2
  38. package/dist/index.cjs +128 -13
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +4 -1
  41. package/dist/index.d.ts +4 -1
  42. package/dist/index.js +4 -4
  43. package/dist/{server-SHOYO52X.js → server-LKVPEQFE.js} +1386 -778
  44. package/dist/server-LKVPEQFE.js.map +1 -0
  45. package/dist/{site-extract-repository-ZDTO7ODZ.js → site-extract-repository-JHMVHENZ.js} +4 -4
  46. package/dist/{worker-BJ64GCDE.js → worker-ZZHSYI3Y.js} +7 -7
  47. package/docs/mcp-tool-manifest.generated.json +213 -3
  48. package/package.json +1 -1
  49. package/dist/chunk-6ZUK4H66.js.map +0 -1
  50. package/dist/chunk-A54ELKJK.js.map +0 -1
  51. package/dist/chunk-AIK7WQD5.js.map +0 -1
  52. package/dist/chunk-F65IIUHI.js +0 -7
  53. package/dist/chunk-F65IIUHI.js.map +0 -1
  54. package/dist/chunk-LKIP7VDN.js.map +0 -1
  55. package/dist/chunk-V73MPRU6.js.map +0 -1
  56. package/dist/server-SHOYO52X.js.map +0 -1
  57. /package/dist/{chunk-C2OD4ELT.js.map → chunk-3HBPKR5G.js.map} +0 -0
  58. /package/dist/{chunk-XL6YIWJ3.js.map → chunk-SUOHUXQS.js.map} +0 -0
  59. /package/dist/{chunk-WJJTGRK4.js.map → chunk-V36LS5YV.js.map} +0 -0
  60. /package/dist/{chunk-AT2PFWHW.js.map → chunk-ZID3WQID.js.map} +0 -0
  61. /package/dist/{db-5SZQUI7A.js.map → db-YAI5AQOI.js.map} +0 -0
  62. /package/dist/{extract-bundle-YNXTFCC3.js.map → extract-bundle-GPZRLBVY.js.map} +0 -0
  63. /package/dist/{site-extract-repository-ZDTO7ODZ.js.map → site-extract-repository-JHMVHENZ.js.map} +0 -0
  64. /package/dist/{worker-BJ64GCDE.js.map → worker-ZZHSYI3Y.js.map} +0 -0
@@ -12,17 +12,17 @@ import {
12
12
  browserServiceProfileSaveChanges,
13
13
  recordVendorUsage,
14
14
  vendorCostUsd
15
- } from "./chunk-WJJTGRK4.js";
15
+ } from "./chunk-V36LS5YV.js";
16
16
  import {
17
17
  DEFAULT_MAPS_PROXY_MODE,
18
18
  DEFAULT_PROXY_MODE
19
- } from "./chunk-V73MPRU6.js";
19
+ } from "./chunk-CB5C3BPB.js";
20
20
  import {
21
21
  PACKAGE_VERSION
22
- } from "./chunk-F65IIUHI.js";
22
+ } from "./chunk-NFMIHEYE.js";
23
23
  import {
24
24
  MC_PER_CREDIT
25
- } from "./chunk-LKIP7VDN.js";
25
+ } from "./chunk-JBSGBSGT.js";
26
26
  import {
27
27
  sanitizeVendorName
28
28
  } from "./chunk-M2S27J6Z.js";
@@ -343,8 +343,11 @@ seam is noted so you can chain them.
343
343
  url the user gives).
344
344
 
345
345
  ## Reddit
346
- - A reddit.com thread/post URL -> **reddit_thread**. Returns the post plus its comment tree and handles
347
- Reddit's bot wall itself (no login needed). Find threads first with \`search_serp\`.
346
+ - Read ONE known reddit.com thread/post URL -> **reddit_thread**. Returns the post plus its comment tree and
347
+ handles Reddit's bot wall itself (no login needed). Find threads first with \`search_serp\` or \`reddit_trending\`.
348
+ - DISCOVER what a niche is talking about (topic, no known thread) -> **reddit_trending** (takes a topic, optional
349
+ subreddit; returns the last 30 days' top threads ranked by engagement plus the questions people asked \u2014
350
+ feed winning \`rankedThreads[].url\` values into \`reddit_thread\` for the full comment tree).
348
351
 
349
352
  ## Other sites & logins (browser agent)
350
353
  For an arbitrary site or a logged-in dashboard with no dedicated tool, use the browser_* agent. **First
@@ -1956,6 +1959,65 @@ ${commentMd || "_No comments captured._"}`
1956
1959
  }
1957
1960
  };
1958
1961
  }
1962
+ function formatRedditTrending(raw, input) {
1963
+ const parsed = parseData(raw);
1964
+ if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
1965
+ const d = parsed.data;
1966
+ const threads = d.rankedThreads ?? [];
1967
+ const questions = (d.questions ?? []).filter((q) => q.question);
1968
+ const totals = {
1969
+ threads: d.totals?.threads ?? threads.length,
1970
+ upvotes: d.totals?.upvotes ?? 0,
1971
+ comments: d.totals?.comments ?? 0
1972
+ };
1973
+ const subreddit = d.subreddit ?? input.subreddit ?? null;
1974
+ const threadBlocks = threads.map((t, i) => [
1975
+ `### ${i + 1}. ${t.title || "Untitled"}`,
1976
+ `**${t.subreddit || "r/?"}** \xB7 ${t.score ?? 0} upvotes \xB7 ${t.commentCount ?? 0} comments \xB7 engagement ${t.engagementScore ?? 0}${t.ageText ? ` \xB7 ${t.ageText}` : ""}`,
1977
+ t.url ? `${t.url}` : "",
1978
+ t.topQuestions?.length ? `**Top questions:**
1979
+ ${t.topQuestions.map((q) => `- ${q}`).join("\n")}` : ""
1980
+ ].filter(Boolean).join("\n")).join("\n\n");
1981
+ const questionList = questions.map((q) => `- ${q.question}${q.threadUrl ? `
1982
+ ${q.threadUrl}` : ""}`).join("\n");
1983
+ const full = [
1984
+ `# Reddit Trending: "${d.topic || input.topic}"${subreddit ? ` in r/${subreddit}` : ""}`,
1985
+ `**${totals.threads} threads \xB7 ${totals.upvotes} upvotes \xB7 ${totals.comments} comments** \xB7 last ${d.window === "7d" ? "week" : "month"} \xB7 ${d.threadsScraped ?? 0} of ${d.candidatesFound ?? threads.length} discovered scraped${d.partial ? " (partial \u2014 hit the time limit)" : ""}`,
1986
+ `
1987
+ ## Ranked threads
1988
+ ${threadBlocks || "_No threads found._"}`,
1989
+ `
1990
+ ## Questions people asked
1991
+ ${questionList || "_No questions extracted._"}`,
1992
+ `
1993
+ ---
1994
+ \u{1F4A1} Deep-dive a winner's full comment tree: pass its url to \`reddit_thread\``
1995
+ ].join("\n");
1996
+ return {
1997
+ ...oneBlock(full),
1998
+ structuredContent: {
1999
+ topic: d.topic ?? input.topic,
2000
+ subreddit,
2001
+ window: d.window ?? "30d",
2002
+ totals,
2003
+ rankedThreads: threads.map((t) => ({
2004
+ title: t.title ?? "",
2005
+ url: t.url ?? "",
2006
+ subreddit: t.subreddit ?? "",
2007
+ score: Number(t.score ?? 0),
2008
+ commentCount: Number(t.commentCount ?? 0),
2009
+ engagementScore: Number(t.engagementScore ?? 0),
2010
+ ageText: t.ageText ?? "",
2011
+ topQuestions: t.topQuestions ?? []
2012
+ })),
2013
+ questions: questions.map((q) => ({ question: q.question ?? "", threadUrl: q.threadUrl ?? "" })),
2014
+ threadsScraped: Number(d.threadsScraped ?? 0),
2015
+ candidatesFound: Number(d.candidatesFound ?? threads.length),
2016
+ partial: Boolean(d.partial),
2017
+ searchQuery: d.searchQuery ?? ""
2018
+ }
2019
+ };
2020
+ }
1959
2021
  function formatFacebookAdSearch(raw, input) {
1960
2022
  const parsed = parseData(raw);
1961
2023
  if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
@@ -3553,6 +3615,14 @@ var RedditThreadInputSchema = {
3553
3615
  url: z.string().min(1).describe("A reddit.com thread/post URL (www, old, new Reddit, or redd.it)."),
3554
3616
  maxComments: z.number().int().min(1).max(2e3).optional().describe("Optional cap on comments returned. Omit to return all captured comments.")
3555
3617
  };
3618
+ var RedditTrendingInputSchema = {
3619
+ topic: z.string().min(1).describe('Topic to scan, in plain words (e.g. "crm for small business"). Not a URL \u2014 pass a known thread URL to reddit_thread instead.'),
3620
+ subreddit: z.string().min(1).optional().describe('Bare subreddit name to scope the scan to one community, e.g. "SEO" (no r/ prefix, no URL). Omit to scan all of Reddit.'),
3621
+ window: z.enum(["week", "month"]).default("month").describe('How recent the threads must be: "week" or "month" (default). Applied via a Google time filter over reddit.com, so it reflects genuine recency.'),
3622
+ maxThreads: z.number().int().min(1).max(40).default(20).describe("How many discovered threads to scrape and rank. Default 20 (scrape-all). Each scraped thread is billed like reddit_thread + its comments, so lower this to cap cost; raise toward 40 for a wider sweep. Scraping runs in parallel and stops early if it nears the request time limit (partial:true in the response)."),
3623
+ includeComments: z.boolean().default(true).describe("Scrape each discovered thread for real upvotes, comments, and the questions people asked, then rank by engagement. Set false for a fast, cheap discovery-only sweep \u2014 returns the discovered threads (title + url) in relevance order with NO engagement stats and NO per-thread billing, so you can then call reddit_thread on the ones you want."),
3624
+ maxCommentsPerThread: z.number().int().min(1).max(200).default(50).describe("Comments captured per scraped thread when includeComments is true. Default 50. Billed per captured comment.")
3625
+ };
3556
3626
  var VideoFrameAnalysisInputSchema = {
3557
3627
  sourceUrl: z.string().min(1).describe("A YouTube, Facebook, Instagram, TikTok, or Vimeo URL (downloaded automatically), or a direct video file URL (.mp4/.webm/.mov). Videos up to 30 minutes are supported."),
3558
3628
  intervalS: z.number().min(1).max(30).optional().describe("Preferred seconds between sampled frames (1-30, default 2). Automatically widened for long videos so the whole duration is covered within the frame budget."),
@@ -4140,6 +4210,34 @@ var RedditThreadOutputSchema = {
4140
4210
  body: z.string()
4141
4211
  }))
4142
4212
  };
4213
+ var RedditTrendingOutputSchema = {
4214
+ topic: z.string(),
4215
+ subreddit: NullableString,
4216
+ window: z.string(),
4217
+ totals: z.object({
4218
+ threads: z.number().int().min(0),
4219
+ upvotes: z.number().int().min(0),
4220
+ comments: z.number().int().min(0)
4221
+ }),
4222
+ rankedThreads: z.array(z.object({
4223
+ title: z.string(),
4224
+ url: z.string(),
4225
+ subreddit: z.string(),
4226
+ score: z.number().int().min(0),
4227
+ commentCount: z.number().int().min(0),
4228
+ engagementScore: z.number().int().min(0),
4229
+ ageText: z.string(),
4230
+ topQuestions: z.array(z.string())
4231
+ })),
4232
+ questions: z.array(z.object({
4233
+ question: z.string(),
4234
+ threadUrl: z.string()
4235
+ })),
4236
+ threadsScraped: z.number().int().min(0),
4237
+ candidatesFound: z.number().int().min(0),
4238
+ partial: z.boolean(),
4239
+ searchQuery: z.string()
4240
+ };
4143
4241
  var FacebookPageIntelOutputSchema = {
4144
4242
  advertiserName: NullableString,
4145
4243
  inputMode: z.enum(["pageId", "libraryId", "query"]),
@@ -4510,7 +4608,8 @@ var SearchSerpInputSchema = {
4510
4608
  proxyMode: z.enum(["configured", "none"]).default(DEFAULT_PROXY_MODE).describe("Leave unset for the default route. Country/region localization comes from gl/hl plus the city or region in the query."),
4511
4609
  proxyZip: z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override."),
4512
4610
  debug: z.boolean().default(false).describe("Include sanitized diagnostics for debugging."),
4513
- pages: z.number().int().min(1).max(2).default(1).describe("Number of result pages to fetch (1\u20132).")
4611
+ pages: z.number().int().min(1).max(2).default(1).describe("Number of result pages to fetch (1\u20132)."),
4612
+ recency: z.enum(["day", "week", "month", "year"]).optional().describe('Restrict results to a recent time window (Google "past day/week/month/year" filter). Omit for all-time. Useful for "what is being said this week" style queries; pairs well with a site: operator in the query.')
4514
4613
  };
4515
4614
  var CaptureSerpSnapshotInputSchema = {
4516
4615
  query: z.string().min(1).describe('Search query to capture. KEEP the place in the query text for localized captures (e.g. "botox clinic austin tx") and also set location.'),
@@ -5438,6 +5537,13 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
5438
5537
  outputSchema: recordOutputSchema("reddit_thread", RedditThreadOutputSchema),
5439
5538
  annotations: liveWebToolAnnotations("Reddit Thread + Comments")
5440
5539
  }, async (input) => formatRedditThread(await executor.redditThread(input), input));
5540
+ server.registerTool("reddit_trending", {
5541
+ title: "Reddit Trending",
5542
+ description: "Discover the top Reddit conversations about a topic from the last week or month: finds relevant recent threads via a Google site:reddit.com search (optionally scoped to one subreddit), scrapes them for real upvotes, comments, and the questions people asked, and ranks by engagement (upvotes + 2x comments). Scraping runs in parallel across the discovered threads; set includeComments:false for a fast, cheap discovery-only sweep (relevant thread list, no engagement stats, no per-thread billing) and then read the ones you want with reddit_thread. Not for reading one known thread URL \u2014 use reddit_thread for that.",
5543
+ inputSchema: RedditTrendingInputSchema,
5544
+ outputSchema: recordOutputSchema("reddit_trending", RedditTrendingOutputSchema),
5545
+ annotations: liveWebToolAnnotations("Reddit Trending")
5546
+ }, async (input) => formatRedditTrending(await executor.redditTrending(input), input));
5441
5547
  server.registerTool("video_frame_analysis", {
5442
5548
  title: "Video Breakdown (frame-by-frame + transcript)",
5443
5549
  description: "Produce a deep frame-by-frame + transcript breakdown of a video \u2014 pacing, hook, visual style, and how to replicate it. Accepts a YouTube, Facebook, Instagram, TikTok, or Vimeo URL directly (downloaded for you), or a direct video file URL (.mp4/.webm/.mov). Costs $1 per 120 frames requested (max 480 = $4; refunded down if the video can't use them; refunded fully on failure): returns a runId immediately; poll video_frame_analysis_status until done. Videos up to 30 minutes.",
@@ -5951,6 +6057,9 @@ var HttpMcpToolExecutor = class {
5951
6057
  redditThread(input) {
5952
6058
  return this.call("/reddit/thread", input, this.httpTimeoutOverrideMs ?? 24e4);
5953
6059
  }
6060
+ redditTrending(input) {
6061
+ return this.call("/reddit/trending", input, this.httpTimeoutOverrideMs ?? 3e5);
6062
+ }
5954
6063
  videoFrameAnalysis(input) {
5955
6064
  return this.call("/video/analyze", input);
5956
6065
  }
@@ -10153,4 +10262,4 @@ export {
10153
10262
  MEMORY_TOOL_SCHEMAS,
10154
10263
  registerMemoryMcpTools
10155
10264
  };
10156
- //# sourceMappingURL=chunk-6ZUK4H66.js.map
10265
+ //# sourceMappingURL=chunk-J32XSMJI.js.map