crw-mcp 0.6.0 → 0.16.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.
package/bin/crw-mcp.js CHANGED
@@ -7,41 +7,133 @@ if (process.argv[2] === "init") {
7
7
  }
8
8
 
9
9
  const { spawnSync } = require("child_process");
10
+ const fs = require("fs");
10
11
  const path = require("path");
12
+ const os = require("os");
13
+ const https = require("https");
11
14
 
15
+ const VERSION = require("../package.json").version;
16
+ const REPO = "us/crw";
17
+
18
+ // Each platform has a prebuilt npm package (fast path, installed via
19
+ // optionalDependencies) AND a matching GitHub release asset (fallback path,
20
+ // downloaded on first run). The fallback makes the launcher robust to a
21
+ // missing/unpublishable platform package (e.g. npm security-held names) — no
22
+ // single package can freeze the channel, mirroring the PyPI launcher.
12
23
  const PLATFORMS = {
13
- "darwin-x64": "crw-mcp-darwin-x64",
14
- "darwin-arm64": "crw-mcp-darwin-arm64",
15
- "linux-x64": "crw-mcp-linux-x64",
16
- "linux-arm64": "crw-mcp-linux-arm64",
17
- "win32-x64": "crw-mcp-win32-x64",
18
- "win32-arm64": "crw-mcp-win32-arm64",
24
+ "darwin-x64": { pkg: "crw-mcp-darwin-x64", asset: "crw-mcp-darwin-x64.tar.gz" },
25
+ "darwin-arm64": { pkg: "crw-mcp-darwin-arm64", asset: "crw-mcp-darwin-arm64.tar.gz" },
26
+ "linux-x64": { pkg: "crw-mcp-linux-x64", asset: "crw-mcp-linux-x64.tar.gz" },
27
+ "linux-arm64": { pkg: "crw-mcp-linux-arm64", asset: "crw-mcp-linux-arm64.tar.gz" },
28
+ "win32-x64": { pkg: "crw-mcp-win32-x64", asset: "crw-mcp-win32-x64.zip" },
29
+ "win32-arm64": { pkg: "crw-mcp-win32-arm64", asset: "crw-mcp-win32-arm64.zip" },
19
30
  };
20
31
 
21
32
  const key = `${process.platform}-${process.arch}`;
22
- const pkg = PLATFORMS[key];
33
+ const plat = PLATFORMS[key];
23
34
 
24
- if (!pkg) {
35
+ if (!plat) {
25
36
  console.error(
26
37
  `crw-mcp: unsupported platform ${key}. Supported: ${Object.keys(PLATFORMS).join(", ")}`
27
38
  );
28
39
  process.exit(1);
29
40
  }
30
41
 
31
- const ext = process.platform === "win32" ? ".exe" : "";
42
+ const binName = `crw-mcp${process.platform === "win32" ? ".exe" : ""}`;
32
43
 
33
- let bin;
34
- try {
35
- bin = path.join(
36
- path.dirname(require.resolve(`${pkg}/package.json`)),
37
- `crw-mcp${ext}`
38
- );
39
- } catch {
40
- console.error(
41
- `crw-mcp: platform package "${pkg}" not found. Try reinstalling:\n npm install crw-mcp`
42
- );
43
- process.exit(1);
44
+ // 1. Explicit override.
45
+ function fromEnv() {
46
+ const p = process.env.CRW_MCP_BINARY || process.env.CRW_BINARY;
47
+ return p && fs.existsSync(p) ? p : null;
48
+ }
49
+
50
+ // 2. Prebuilt platform package (fast path — no download).
51
+ function fromPackage() {
52
+ try {
53
+ const bin = path.join(
54
+ path.dirname(require.resolve(`${plat.pkg}/package.json`)),
55
+ binName
56
+ );
57
+ return fs.existsSync(bin) ? bin : null;
58
+ } catch {
59
+ return null;
60
+ }
61
+ }
62
+
63
+ function cacheDir() {
64
+ const base =
65
+ process.platform === "win32"
66
+ ? process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local")
67
+ : process.env.XDG_CACHE_HOME || path.join(os.homedir(), ".cache");
68
+ return path.join(base, "crw-mcp", `v${VERSION}`);
69
+ }
70
+
71
+ function httpsGet(url, dest, redirects = 0) {
72
+ return new Promise((resolve, reject) => {
73
+ https
74
+ .get(url, { headers: { "User-Agent": `crw-mcp/${VERSION}` } }, (res) => {
75
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
76
+ res.resume();
77
+ if (redirects > 5) return reject(new Error("too many redirects"));
78
+ return resolve(httpsGet(res.headers.location, dest, redirects + 1));
79
+ }
80
+ if (res.statusCode !== 200) {
81
+ res.resume();
82
+ return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
83
+ }
84
+ const file = fs.createWriteStream(dest);
85
+ res.pipe(file);
86
+ file.on("finish", () => file.close(() => resolve()));
87
+ file.on("error", reject);
88
+ })
89
+ .on("error", reject);
90
+ });
91
+ }
92
+
93
+ // 3. Download the matching release asset and extract it (cached per version).
94
+ async function fromDownload() {
95
+ const dir = cacheDir();
96
+ const bin = path.join(dir, binName);
97
+ if (fs.existsSync(bin)) return bin;
98
+
99
+ fs.mkdirSync(dir, { recursive: true });
100
+ const archive = path.join(dir, plat.asset);
101
+ const url = `https://github.com/${REPO}/releases/download/v${VERSION}/${plat.asset}`;
102
+
103
+ // stderr only: stdout is the MCP (JSON-RPC) channel.
104
+ console.error(`crw-mcp: downloading ${plat.asset} (v${VERSION})...`);
105
+ await httpsGet(url, archive);
106
+
107
+ const tarArgs = plat.asset.endsWith(".zip")
108
+ ? ["-xf", archive, "-C", dir]
109
+ : ["-xzf", archive, "-C", dir];
110
+ const x = spawnSync("tar", tarArgs, { stdio: "inherit" });
111
+ if (x.status !== 0) {
112
+ throw new Error(`failed to extract ${plat.asset} (tar exit ${x.status})`);
113
+ }
114
+ fs.rmSync(archive, { force: true });
115
+
116
+ if (!fs.existsSync(bin)) {
117
+ throw new Error(`binary ${binName} not found in ${plat.asset}`);
118
+ }
119
+ if (process.platform !== "win32") fs.chmodSync(bin, 0o755);
120
+ return bin;
121
+ }
122
+
123
+ async function resolveBinary() {
124
+ return fromEnv() || fromPackage() || (await fromDownload());
44
125
  }
45
126
 
46
- const result = spawnSync(bin, process.argv.slice(2), { stdio: "inherit" });
47
- process.exit(result.status ?? 1);
127
+ resolveBinary()
128
+ .then((bin) => {
129
+ const result = spawnSync(bin, process.argv.slice(2), { stdio: "inherit" });
130
+ process.exit(result.status ?? 1);
131
+ })
132
+ .catch((err) => {
133
+ console.error(
134
+ `crw-mcp: could not locate or download the ${key} binary.\n ${err.message}\n` +
135
+ ` Set CRW_MCP_BINARY=/path/to/crw-mcp to use a local build, or install\n` +
136
+ ` from https://github.com/${REPO}/releases.`
137
+ );
138
+ process.exit(1);
139
+ });
package/bin/init.js CHANGED
@@ -123,11 +123,13 @@ Without flags, auto-detects installed agents and installs to all of them.
123
123
  console.log(` export CRW_API_KEY=${apiKey}`);
124
124
  } else {
125
125
  console.log("\nCloud mode (fastcrw.com):");
126
+ console.log(" 500 free one-time credits, managed infra — https://fastcrw.com");
126
127
  console.log(" export CRW_API_KEY=fc-your-key");
127
- console.log(" export CRW_API_URL=https://fastcrw.com/api");
128
+ console.log(" export CRW_API_URL=https://api.fastcrw.com");
129
+ console.log(" Terms of Service: https://fastcrw.com/terms");
128
130
  }
129
131
 
130
- console.log("\nLocal mode (no key needed):");
132
+ console.log("\nLocal mode (free, same binary, fully capable):");
131
133
  console.log(" npx crw-mcp");
132
134
  console.log("\nDocs: https://fastcrw.com/docs");
133
135
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "crw-mcp",
3
- "version": "0.6.0",
4
- "description": "MCP server for CRW web scraper — scrape, crawl, and map tools for AI agents",
3
+ "version": "0.16.0",
4
+ "description": "MCP server for CRW web scraper — scrape, crawl, map, search, and PDF-parse tools for AI agents",
5
5
  "license": "AGPL-3.0",
6
6
  "homepage": "https://github.com/us/crw",
7
7
  "repository": {
@@ -21,17 +21,18 @@
21
21
  "bin": {
22
22
  "crw-mcp": "bin/crw-mcp.js"
23
23
  },
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
24
27
  "files": [
25
28
  "bin/crw-mcp.js",
26
29
  "bin/init.js",
27
30
  "skills/SKILL.md"
28
31
  ],
29
32
  "optionalDependencies": {
30
- "crw-mcp-darwin-x64": "0.3.5",
31
- "crw-mcp-darwin-arm64": "0.3.5",
32
- "crw-mcp-linux-x64": "0.3.5",
33
- "crw-mcp-linux-arm64": "0.3.5",
34
- "crw-mcp-win32-x64": "0.3.5",
35
- "crw-mcp-win32-arm64": "0.3.5"
33
+ "crw-mcp-darwin-x64": "0.16.0",
34
+ "crw-mcp-darwin-arm64": "0.16.0",
35
+ "crw-mcp-linux-x64": "0.16.0",
36
+ "crw-mcp-linux-arm64": "0.16.0"
36
37
  }
37
38
  }
package/skills/SKILL.md CHANGED
@@ -36,19 +36,22 @@ This installs the CRW skill and MCP server to all detected AI agents (Claude Cod
36
36
 
37
37
  ## MCP Tools
38
38
 
39
+ > **Output bounds:** By default, content is truncated to ~15 000 chars (`crw_scrape`, `crw_check_crawl_status`, `crw_parse_file`) and `crw_map` returns ≤ 100 URLs. Truncated results carry a `truncated: true` marker (`crw_map` also adds `totalDiscovered`). Pass `maxLength: 0` or `limit: 0` to opt out of bounding.
40
+
39
41
  ### crw_scrape
40
42
 
41
43
  Scrape a single URL and return clean content.
42
44
 
43
45
  Parameters:
44
46
  - `url` (required) — The URL to scrape
45
- - `formats` — Output formats: `markdown` (default), `html`, `rawHtml`, `plainText`, `links`, `json`
47
+ - `formats` — Output formats: `markdown` (default), `html`, `links`
46
48
  - `onlyMainContent` — Strip navs/footers/sidebars. Default: `true`
49
+ - `includeTags` — Only include content matching these CSS selectors (e.g. `["article", "main"]`)
50
+ - `excludeTags` — Exclude content matching these CSS selectors (e.g. `["nav", "footer"]`)
47
51
  - `renderJs` — Force JavaScript rendering. Default: auto-detect (null)
48
- - `cssSelector` — Extract only elements matching this CSS selector
49
- - `xpath` — Extract only elements matching this XPath
50
- - `includeTags` — Only include these HTML tags (e.g. `["article", "main"]`)
51
- - `excludeTags` — Remove these HTML tags (e.g. `["nav", "footer"]`)
52
+ - `waitFor` — Milliseconds to wait after page load before capturing
53
+ - `renderer` — Renderer override (e.g. `"playwright"`)
54
+ - `maxLength` — Truncate output to this many chars. `0` = unbounded. Default: ~15 000
52
55
 
53
56
  ### crw_crawl
54
57
 
@@ -56,8 +59,12 @@ Start an async BFS crawl from a URL. Returns a job ID — poll with `crw_check_c
56
59
 
57
60
  Parameters:
58
61
  - `url` (required) — Starting URL
59
- - `maxDepth` — Maximum link depth. Default: `2`, max: `10`
60
- - `limit` — Maximum pages to crawl. Default: `10`, max: `1000`
62
+ - `maxDepth` — Maximum link depth. Default: `2`
63
+ - `maxPages` — Maximum pages to crawl
64
+ - `jsonSchema` — JSON schema for structured extraction per page
65
+ - `renderJs` — Force JavaScript rendering
66
+ - `waitFor` — Milliseconds to wait after page load before capturing
67
+ - `renderer` — Renderer override
61
68
 
62
69
  Returns: `{ "id": "job-uuid" }` — use this ID with crw_check_crawl_status.
63
70
 
@@ -67,18 +74,22 @@ Poll an async crawl job for results.
67
74
 
68
75
  Parameters:
69
76
  - `id` (required) — The crawl job ID from `crw_crawl`
77
+ - `maxLength` — Truncate each page's content fields to this many chars. `0` = unbounded. Default: ~15 000
70
78
 
71
79
  Returns: `{ "status": "pending|running|completed|failed", "data": [...] }`
72
80
 
73
81
  ### crw_search
74
82
 
75
- Search the web and return relevant results with titles, URLs, and descriptions.
83
+ Search the web and return relevant results with titles, URLs, and descriptions. Always available in proxy/cloud mode; in embedded mode only when a SearXNG backend is configured.
76
84
 
77
85
  Parameters:
78
86
  - `query` (required) — The search query
79
87
  - `limit` — Maximum number of results to return. Default: `5`
80
88
  - `lang` — Language code for results (e.g. `"en"`, `"tr"`)
81
89
  - `country` — Country code for results (e.g. `"us"`, `"tr"`)
90
+ - `tbs` — Time filter: `qdr:h|qdr:d|qdr:w|qdr:m|qdr:y` (past hour/day/week/month/year)
91
+ - `sources` — If set, group results by source: `web`, `news`, `images`
92
+ - `categories` — Bias toward a category (e.g. `"pdf"`, `"github"`, `"research"`, or a native SearXNG category)
82
93
  - `scrapeOptions` — Options for scraping each result page (e.g. `{"formats": ["markdown"]}`)
83
94
 
84
95
  ### crw_map
@@ -89,8 +100,22 @@ Parameters:
89
100
  - `url` (required) — The URL to map
90
101
  - `maxDepth` — Discovery depth. Default: `2`
91
102
  - `useSitemap` — Check sitemap.xml. Default: `true`
103
+ - `crawlFallback` — Supplement sitemap discovery with a short BFS crawl. Default: `true` (`false` = sitemap-only)
104
+ - `limit` — Maximum URLs to return. `0` = unbounded. Default: `100`
105
+
106
+ Returns: `{ "links": ["url1", "url2", ...] }`
107
+
108
+ ### crw_parse_file
92
109
 
93
- Returns: `{ "links": ["url1", "url2", ...] }` up to 5000 URLs.
110
+ Parse a local file (PDF) into markdown or structured output without fetching from the web.
111
+
112
+ Parameters:
113
+ - `contentBase64` (required) — Base64-encoded file contents
114
+ - `filename` — Original filename (optional, e.g. `"report.pdf"`)
115
+ - `formats` — Output formats: `markdown` (default), `plainText`, `links`, `json`, `summary` (json/summary need a server LLM)
116
+ - `jsonSchema` — JSON schema for LLM extraction (when `formats` includes `json`)
117
+ - `parsers` — Document parsers to apply. Default: `["pdf"]`
118
+ - `maxLength` — Truncate output to this many chars. `0` = unbounded. Default: ~15 000
94
119
 
95
120
  ## Common Patterns
96
121
 
@@ -103,7 +128,7 @@ crw_scrape(url="https://example.com", formats=["markdown"])
103
128
  First discover URLs, then crawl:
104
129
  ```
105
130
  crw_map(url="https://docs.example.com") → get URL list
106
- crw_crawl(url="https://docs.example.com", limit=50) → extract all content
131
+ crw_crawl(url="https://docs.example.com", maxPages=50) → extract all content
107
132
  crw_check_crawl_status(id="...") → poll until completed
108
133
  ```
109
134