crw-mcp 0.18.0 → 0.18.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.
package/bin/agents.js ADDED
@@ -0,0 +1,149 @@
1
+ // Shared agent registry + installers for `crw-mcp init` (skill only) and
2
+ // `crw-mcp install` (skill + MCP server). MCP config shapes are per-agent and
3
+ // were verified against each tool's official docs/source — DO NOT "normalize"
4
+ // them: Codex is TOML with `mcp_servers`, OpenCode uses `mcp`/`type:local`/
5
+ // `command:[]`/`environment`, the rest are JSON `mcpServers`.
6
+
7
+ const fs = require("fs");
8
+ const path = require("path");
9
+ const os = require("os");
10
+ const { spawnSync } = require("child_process");
11
+
12
+ const home = os.homedir();
13
+
14
+ // kind: how the MCP server is registered.
15
+ // - "claude-cli": shell out to `claude mcp add-json` (handles ~/.claude.json)
16
+ // - "json": merge file[topKey].crw = { [type], command, env }
17
+ // - "toml-codex": append [mcp_servers.crw] to ~/.codex/config.toml
18
+ // - "opencode": merge file.mcp.crw = { type:local, command:[], environment }
19
+ const AGENTS = [
20
+ { name: "Claude Code", flag: "--claude-code", configDir: ".claude",
21
+ mcp: { kind: "claude-cli" } },
22
+ { name: "Cursor", flag: "--cursor", configDir: ".cursor",
23
+ mcp: { kind: "json", file: ".cursor/mcp.json", withType: true } },
24
+ { name: "Gemini CLI", flag: "--gemini-cli", configDir: ".gemini",
25
+ mcp: { kind: "json", file: ".gemini/settings.json", withType: false } },
26
+ { name: "Codex", flag: "--codex", configDir: ".codex",
27
+ mcp: { kind: "toml-codex", file: ".codex/config.toml" } },
28
+ { name: "OpenCode", flag: "--opencode", configDir: ".opencode",
29
+ mcp: { kind: "opencode", file: path.join(".config", "opencode", "opencode.json") } },
30
+ { name: "Windsurf", flag: "--windsurf", configDir: ".codeium",
31
+ mcp: { kind: "json", file: path.join(".codeium", "windsurf", "mcp_config.json"), withType: false } },
32
+ ];
33
+
34
+ function detectAgents(args) {
35
+ const has = (f) => args.includes(f);
36
+ const explicit = AGENTS.filter((a) => has(a.flag));
37
+ if (explicit.length) return explicit;
38
+ // --all or no flag → every agent whose config dir exists.
39
+ return AGENTS.filter((a) => fs.existsSync(path.join(home, a.configDir)));
40
+ }
41
+
42
+ function getApiKey(args) {
43
+ const i = args.indexOf("--api-key");
44
+ return i !== -1 && args[i + 1] ? args[i + 1] : process.env.CRW_API_KEY || null;
45
+ }
46
+
47
+ function readSkill() {
48
+ return fs.readFileSync(path.join(__dirname, "..", "skills", "SKILL.md"), "utf-8");
49
+ }
50
+
51
+ /** Write the SKILL.md into the agent's skills/crw/ dir. Returns the path. */
52
+ function installSkill(agent, skillContent) {
53
+ const dir = path.join(home, agent.configDir, "skills", "crw");
54
+ fs.mkdirSync(dir, { recursive: true });
55
+ const file = path.join(dir, "SKILL.md");
56
+ fs.writeFileSync(file, skillContent, "utf-8");
57
+ return file;
58
+ }
59
+
60
+ function readJson(file) {
61
+ try {
62
+ return JSON.parse(fs.readFileSync(file, "utf-8"));
63
+ } catch {
64
+ return {};
65
+ }
66
+ }
67
+
68
+ function writeJson(file, obj) {
69
+ fs.mkdirSync(path.dirname(file), { recursive: true });
70
+ fs.writeFileSync(file, JSON.stringify(obj, null, 2) + "\n", "utf-8");
71
+ }
72
+
73
+ /**
74
+ * Register the crw MCP server for one agent. `env` is {} for local mode (the
75
+ * embedded binary needs no config) or {CRW_API_KEY, CRW_API_URL} for cloud.
76
+ * Returns a short human label of what was done.
77
+ */
78
+ function installMcp(agent, env) {
79
+ const m = agent.mcp;
80
+ const hasEnv = Object.keys(env).length > 0;
81
+
82
+ if (m.kind === "claude-cli") {
83
+ // add-json avoids the `-e` variadic-name pitfall and merges ~/.claude.json
84
+ // (user scope) correctly. Idempotent-ish: remove a stale entry first.
85
+ const server = { command: "crw-mcp", ...(hasEnv ? { env } : {}) };
86
+ spawnSync("claude", ["mcp", "remove", "--scope", "user", "crw"], { stdio: "ignore" });
87
+ const r = spawnSync(
88
+ "claude",
89
+ ["mcp", "add-json", "--scope", "user", "crw", JSON.stringify(server)],
90
+ { stdio: "ignore" },
91
+ );
92
+ if (r.error || r.status !== 0) {
93
+ return "MCP: run `claude mcp add-json --scope user crw '" + JSON.stringify(server) + "'` (claude CLI not found)";
94
+ }
95
+ return "MCP via claude CLI (user scope)";
96
+ }
97
+
98
+ if (m.kind === "json") {
99
+ const file = path.join(home, m.file);
100
+ const cfg = readJson(file);
101
+ cfg.mcpServers = cfg.mcpServers || {};
102
+ cfg.mcpServers.crw = {
103
+ ...(m.withType ? { type: "stdio" } : {}),
104
+ command: "crw-mcp",
105
+ ...(hasEnv ? { env } : {}),
106
+ };
107
+ writeJson(file, cfg);
108
+ return `MCP → ${m.file}`;
109
+ }
110
+
111
+ if (m.kind === "opencode") {
112
+ const file = path.join(home, m.file);
113
+ const cfg = readJson(file);
114
+ if (!cfg.$schema) cfg.$schema = "https://opencode.ai/config.json";
115
+ cfg.mcp = cfg.mcp || {};
116
+ cfg.mcp.crw = {
117
+ type: "local",
118
+ command: ["crw-mcp"],
119
+ ...(hasEnv ? { environment: env } : {}),
120
+ };
121
+ writeJson(file, cfg);
122
+ return `MCP → ${m.file}`;
123
+ }
124
+
125
+ if (m.kind === "toml-codex") {
126
+ const file = path.join(home, m.file);
127
+ let toml = "";
128
+ try {
129
+ toml = fs.readFileSync(file, "utf-8");
130
+ } catch {
131
+ /* new file */
132
+ }
133
+ if (/\[mcp_servers\.crw\]/.test(toml)) {
134
+ return `MCP already in ${m.file} (left as-is)`;
135
+ }
136
+ const envLines = hasEnv
137
+ ? "\n[mcp_servers.crw.env]\n" +
138
+ Object.entries(env).map(([k, v]) => `${k} = "${v}"`).join("\n") + "\n"
139
+ : "";
140
+ const block = `\n[mcp_servers.crw]\ncommand = "crw-mcp"\n${envLines}`;
141
+ fs.mkdirSync(path.dirname(file), { recursive: true });
142
+ fs.appendFileSync(file, (toml && !toml.endsWith("\n") ? "\n" : "") + block, "utf-8");
143
+ return `MCP → ${m.file}`;
144
+ }
145
+
146
+ return "MCP: unsupported";
147
+ }
148
+
149
+ module.exports = { AGENTS, detectAgents, getApiKey, readSkill, installSkill, installMcp, home };
package/bin/install.js ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+
3
+ // `crw-mcp install [--<agent>] [--api-key <key>] [--api-url <url>]`
4
+ // Installs BOTH the crw skill AND the crw MCP server into the target agents.
5
+ // (`crw-mcp init` installs the skill only.) Without an agent flag it targets
6
+ // every detected agent; with no API key it wires local mode (free, embedded).
7
+
8
+ const {
9
+ AGENTS,
10
+ detectAgents,
11
+ getApiKey,
12
+ readSkill,
13
+ installSkill,
14
+ installMcp,
15
+ home,
16
+ } = require("./agents.js");
17
+
18
+ const args = process.argv.slice(2);
19
+
20
+ function argValue(flag) {
21
+ const i = args.indexOf(flag);
22
+ return i !== -1 && args[i + 1] ? args[i + 1] : null;
23
+ }
24
+
25
+ if (args.includes("--help") || args.includes("-h")) {
26
+ console.log(`
27
+ crw-mcp install — Install the CRW skill AND MCP server into your AI agents
28
+
29
+ Usage:
30
+ npx crw-mcp@latest install [options]
31
+
32
+ Options:
33
+ --all Install to all detected agents
34
+ --claude-code Claude Code --codex Codex
35
+ --cursor Cursor --opencode OpenCode
36
+ --gemini-cli Gemini CLI --windsurf Windsurf
37
+ --api-key <key> fastcrw.com API key → cloud mode (else local/embedded)
38
+ --api-url <url> API base (default https://api.fastcrw.com when a key is set)
39
+ -h, --help Show this help
40
+
41
+ Without flags, auto-detects installed agents. Installs the skill (SKILL.md) and
42
+ registers the "crw" MCP server. Run \`crw-mcp init\` for the skill only.
43
+ `);
44
+ process.exit(0);
45
+ }
46
+
47
+ const apiKey = getApiKey(args);
48
+ const apiUrl = argValue("--api-url") || (apiKey ? "https://api.fastcrw.com" : null);
49
+ const env = apiKey ? { CRW_API_KEY: apiKey, CRW_API_URL: apiUrl } : {};
50
+
51
+ const agents = detectAgents(args);
52
+ if (agents.length === 0) {
53
+ console.log("No supported AI agents detected.");
54
+ console.log("Supported: " + AGENTS.map((a) => a.name).join(", "));
55
+ console.log("\nPass an agent flag (e.g. --claude-code) to install anyway.");
56
+ process.exit(1);
57
+ }
58
+
59
+ const skill = readSkill();
60
+ const results = [];
61
+ for (const agent of agents) {
62
+ try {
63
+ const skillPath = installSkill(agent, skill);
64
+ const mcpLabel = installMcp(agent, env);
65
+ results.push({ name: agent.name, skillPath, mcpLabel });
66
+ } catch (err) {
67
+ console.error(` ${agent.name}: ${err.message}`);
68
+ }
69
+ }
70
+
71
+ if (results.length === 0) {
72
+ console.log("Failed to install to any agent.");
73
+ process.exit(1);
74
+ }
75
+
76
+ console.log("crw installed — skill + MCP:\n");
77
+ for (const r of results) {
78
+ console.log(` ${r.name}`);
79
+ console.log(` skill: ${r.skillPath.replace(home, "~")}`);
80
+ console.log(` ${r.mcpLabel.replace(home, "~")}`);
81
+ }
82
+
83
+ if (apiKey) {
84
+ console.log(`\nMode: cloud — CRW_API_KEY ${apiKey.slice(0, 10)}… → ${apiUrl}`);
85
+ } else {
86
+ console.log("\nMode: local (free, embedded binary — no key needed).");
87
+ console.log(" Cloud mode: re-run with --api-key crw_live_… (500 free credits at https://fastcrw.com).");
88
+ }
89
+ console.log("\nRestart your agent to load the MCP server.");
90
+ console.log("Docs: https://fastcrw.com/docs");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crw-mcp",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
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",
@@ -27,12 +27,14 @@
27
27
  "files": [
28
28
  "bin/crw-mcp.js",
29
29
  "bin/init.js",
30
+ "bin/install.js",
31
+ "bin/agents.js",
30
32
  "skills/SKILL.md"
31
33
  ],
32
34
  "optionalDependencies": {
33
- "crw-mcp-darwin-x64": "0.18.0",
34
- "crw-mcp-darwin-arm64": "0.18.0",
35
- "crw-mcp-linux-x64": "0.18.0",
36
- "crw-mcp-linux-arm64": "0.18.0"
35
+ "crw-mcp-darwin-x64": "0.18.2",
36
+ "crw-mcp-darwin-arm64": "0.18.2",
37
+ "crw-mcp-linux-x64": "0.18.2",
38
+ "crw-mcp-linux-arm64": "0.18.2"
37
39
  }
38
40
  }