scrapeunblocker-mcp 0.2.1 → 0.2.2

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 (2) hide show
  1. package/dist/index.js +21 -6
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
6
  import { z } from "zod";
7
7
  import { APIError, ScrapeUnblockerClient } from "scrapeunblocker";
8
- var VERSION = "0.2.1";
8
+ var VERSION = "0.2.2";
9
9
  var DEFAULT_BASE_URL = "https://api.scrapeunblocker.com";
10
10
  var API_KEY_HEADER = "x-scrapeunblocker-key";
11
11
  function resolveApiKey() {
@@ -45,11 +45,26 @@ ${page}` : `${head}
45
45
 
46
46
  The target sent no page body.`;
47
47
  }
48
+ function isNoDataExtracted(status, body) {
49
+ if (status !== 422 || !body) return false;
50
+ try {
51
+ return JSON.parse(body)?.error === "no_data_extracted";
52
+ } catch {
53
+ return false;
54
+ }
55
+ }
56
+ function noDataExtractedText(url) {
57
+ return `No structured data could be extracted from ${url}: the page loaded, but nothing on it matched a structured shape. This call was not billed. To get the page itself, call fetch_html for the same URL.`;
58
+ }
48
59
  function errorResult(url, err) {
49
- const gone = err instanceof APIError ? targetGoneStatus(err.statusCode) : null;
50
- if (gone !== null) {
51
- const body = err instanceof APIError ? err.body ?? "" : "";
52
- return { content: [{ type: "text", text: targetGoneText(url, gone, body) }] };
60
+ if (err instanceof APIError) {
61
+ const gone = targetGoneStatus(err.statusCode);
62
+ if (gone !== null) {
63
+ return { content: [{ type: "text", text: targetGoneText(url, gone, err.body ?? "") }] };
64
+ }
65
+ if (isNoDataExtracted(err.statusCode, err.body)) {
66
+ return { content: [{ type: "text", text: noDataExtractedText(url) }] };
67
+ }
53
68
  }
54
69
  return { content: [{ type: "text", text: errorText(err) }], isError: true };
55
70
  }
@@ -277,7 +292,7 @@ server.registerTool(
277
292
  "fetch_parsed",
278
293
  {
279
294
  title: "Fetch AI-parsed page data",
280
- description: "Fetch a web page through ScrapeUnblocker and return AI-parsed structured JSON instead of raw HTML (e.g. product details, article content). Best for extracting fields from product, listing or article pages without writing your own HTML parsing.",
295
+ description: "Fetch a web page through ScrapeUnblocker and return AI-parsed structured JSON instead of raw HTML (e.g. product details, article content). Best for extracting fields from product, listing or article pages without writing your own HTML parsing. If the page holds no structured data, the result says so (that call is not billed) - use fetch_html for the page itself.",
281
296
  inputSchema: {
282
297
  url: z.string().url().describe("The absolute URL to fetch and parse."),
283
298
  proxy_country: z.string().length(2).optional().describe("Optional ISO country code to route through, e.g. 'US'."),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scrapeunblocker-mcp",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
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) through the ScrapeUnblocker anti-bot API, using your own API key.",
6
6
  "keywords": [