scrapeunblocker-mcp 0.3.5 → 0.4.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 (3) hide show
  1. package/README.md +3 -1
  2. package/dist/index.js +79 -21
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -18,6 +18,7 @@ You bring your **own** API key. Nothing is shared or proxied through us.
18
18
  | `google_search` | Run a Google search and return organic results as JSON. |
19
19
  | `list_plugins` | List the ready-made site plugins (100+: Amazon, Walmart, Zillow, Booking.com, Indeed, Google Trends, YouTube, App Store, Ahrefs, ...) with their parameters. Free - it calls nothing. |
20
20
  | `run_plugin` | Run one site plugin and return its structured JSON. |
21
+ | `amazon_search`, `amazon_product`, `amazon_reviews`, `walmart_search`, `ebay_search`, `google_shopping`, `google_local`, `maps_place_details`, `google_images`, `youtube_search`, `youtube_transcript`, `zillow_search`, `indeed_jobs`, `trustpilot_reviews`, `booking_hotels` | The most-used site plugins as tools of their own, each with its parameters in the tool schema - one call, no `list_plugins` lookup. Same JSON as `run_plugin`. |
21
22
 
22
23
  ### Site plugins (structured JSON for known sites)
23
24
 
@@ -25,7 +26,8 @@ For many popular sites there is a plugin that returns the data already
25
26
  parsed - a Target search, a Zillow listing search, an app's App Store reviews,
26
27
  Google Trends for a few keywords. The model calls `list_plugins` (optionally
27
28
  with `search: "zillow"` or a `category`) to find the plugin and its
28
- parameters, then `run_plugin` with `name` and `params`:
29
+ parameters, then `run_plugin` with `name` and `params` (the most-used plugins, listed in
30
+ the table above, can also be called directly as their own tools):
29
31
 
30
32
  ```json
31
33
  { "name": "target_search", "params": { "query": "coffee maker", "max_results": 20 } }
package/dist/index.js CHANGED
@@ -5506,6 +5506,23 @@ var MAX_OUTPUT_CHARS = 1e5;
5506
5506
  var BY_NAME = new Map(PLUGINS.map((p) => [p.name, p]));
5507
5507
  var NAMES = PLUGINS.map((p) => p.name);
5508
5508
  var CATEGORIES2 = CATEGORIES;
5509
+ var FEATURED_PLUGINS = [
5510
+ "amazon_search",
5511
+ "amazon_product",
5512
+ "amazon_reviews",
5513
+ "walmart_search",
5514
+ "ebay_search",
5515
+ "google_shopping",
5516
+ "google_local",
5517
+ "maps_place_details",
5518
+ "google_images",
5519
+ "youtube_search",
5520
+ "youtube_transcript",
5521
+ "zillow_search",
5522
+ "indeed_jobs",
5523
+ "trustpilot_reviews",
5524
+ "booking_hotels"
5525
+ ];
5509
5526
  var text = (t, isError = false) => isError ? { content: [{ type: "text", text: t }], isError: true } : { content: [{ type: "text", text: t }] };
5510
5527
  function signature(p) {
5511
5528
  return `${p.name}(${p.params.map((q) => q.required ? `${q.name}*` : q.name).join(", ")})`;
@@ -5564,8 +5581,49 @@ function prepare(p, params) {
5564
5581
  }
5565
5582
  return out;
5566
5583
  }
5584
+ function paramType(q) {
5585
+ const scalar = (t) => t === "integer" ? z.number().int() : t === "number" ? z.number() : t === "boolean" ? z.boolean() : z.string();
5586
+ let type;
5587
+ if (q.type === "array") {
5588
+ const item = scalar(q.items);
5589
+ type = z.union([z.array(item), item]);
5590
+ } else if (q.enum && q.enum.length && q.enum.every((v) => typeof v === "string")) {
5591
+ type = z.enum(q.enum);
5592
+ if (q.join) type = z.union([type, z.array(type)]);
5593
+ } else {
5594
+ type = scalar(q.type);
5595
+ if (q.join) type = z.union([type, z.array(type)]);
5596
+ }
5597
+ const extras = [
5598
+ q.enum && !q.enum.every((v) => typeof v === "string") ? `One of: ${q.enum.join(", ")}.` : "",
5599
+ q.default !== void 0 ? `Default: ${String(q.default)}.` : ""
5600
+ ].filter(Boolean);
5601
+ const description = [q.description, ...extras].filter(Boolean).join(" ");
5602
+ if (description) type = type.describe(description);
5603
+ return q.required ? type : type.optional();
5604
+ }
5567
5605
  function registerPluginTools(server2, options) {
5568
5606
  const annotations = { readOnlyHint: true, openWorldHint: true, destructiveHint: false, ...options.annotations };
5607
+ async function run(plugin, raw) {
5608
+ const blocked = options.blocked?.();
5609
+ if (blocked) return blocked;
5610
+ const params = prepare(plugin, raw);
5611
+ if (typeof params === "string") return text(`${params}
5612
+
5613
+ ${details(plugin)}`, true);
5614
+ try {
5615
+ const result = await options.call(plugin.path, params);
5616
+ const body = JSON.stringify(result);
5617
+ if (body.length <= MAX_OUTPUT_CHARS) return text(body);
5618
+ return text(
5619
+ body.slice(0, MAX_OUTPUT_CHARS) + `
5620
+
5621
+ [Cut at ${MAX_OUTPUT_CHARS} of ${body.length} characters - ask for fewer results (max_results) or a narrower filter to get the whole answer.]`
5622
+ );
5623
+ } catch (err) {
5624
+ return text(options.errorText(err), true);
5625
+ }
5626
+ }
5569
5627
  server2.registerTool(
5570
5628
  "list_plugins",
5571
5629
  {
@@ -5604,32 +5662,32 @@ function registerPluginTools(server2, options) {
5604
5662
  params: z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.array(z.union([z.string(), z.number()]))])).optional().describe(`The plugin's parameters, e.g. {"query": "coffee maker", "max_results": 20}.`)
5605
5663
  }
5606
5664
  },
5607
- async (args) => {
5608
- const blocked = options.blocked?.();
5609
- if (blocked) return blocked;
5610
- const plugin = BY_NAME.get(args.name);
5611
- const params = prepare(plugin, args.params ?? {});
5612
- if (typeof params === "string") return text(`${params}
5613
-
5614
- ${details(plugin)}`, true);
5615
- try {
5616
- const result = await options.call(plugin.path, params);
5617
- const body = JSON.stringify(result);
5618
- if (body.length <= MAX_OUTPUT_CHARS) return text(body);
5619
- return text(
5620
- body.slice(0, MAX_OUTPUT_CHARS) + `
5621
-
5622
- [Cut at ${MAX_OUTPUT_CHARS} of ${body.length} characters - ask for fewer results (max_results) or a narrower filter to get the whole answer.]`
5665
+ async (args) => run(BY_NAME.get(args.name), args.params ?? {})
5666
+ );
5667
+ for (const name of FEATURED_PLUGINS) {
5668
+ const plugin = BY_NAME.get(name);
5669
+ if (!plugin) continue;
5670
+ const shape = Object.fromEntries(plugin.params.map((q) => [q.name, paramType(q)]));
5671
+ server2.registerTool(
5672
+ plugin.name,
5673
+ {
5674
+ title: plugin.title,
5675
+ annotations: { title: plugin.title, ...annotations },
5676
+ description: `${plugin.description} Returns JSON. Billed like an API request; listing plugins bill one unit per result page they fetch, so keep max_results modest.`,
5677
+ inputSchema: shape
5678
+ },
5679
+ async (args) => {
5680
+ const raw = Object.fromEntries(
5681
+ Object.entries(args).filter(([, v]) => v !== void 0)
5623
5682
  );
5624
- } catch (err) {
5625
- return text(options.errorText(err), true);
5683
+ return run(plugin, raw);
5626
5684
  }
5627
- }
5628
- );
5685
+ );
5686
+ }
5629
5687
  }
5630
5688
 
5631
5689
  // src/index.ts
5632
- var VERSION = "0.3.5";
5690
+ var VERSION = "0.4.0";
5633
5691
  var DEFAULT_BASE_URL = "https://api.scrapeunblocker.com";
5634
5692
  var API_KEY_HEADER = "x-scrapeunblocker-key";
5635
5693
  function resolveApiKey() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scrapeunblocker-mcp",
3
- "version": "0.3.5",
3
+ "version": "0.4.0",
4
4
  "mcpName": "com.scrapeunblocker/scrapeunblocker-mcp",
5
5
  "description": "Model Context Protocol (MCP) server for ScrapeUnblocker - lets Claude fetch any web page's HTML (or AI-parsed JSON) and run 100+ site plugins that return structured JSON, through the ScrapeUnblocker anti-bot API, using your own API key.",
6
6
  "keywords": [