@stealthmole/mcp 0.1.13 → 0.1.14

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/README.md CHANGED
@@ -4,6 +4,16 @@ Connect [Claude Desktop](https://claude.ai/download), [Claude Code](https://docs
4
4
 
5
5
  ## Quick Start
6
6
 
7
+ ### All MCP clients at once
8
+
9
+ Register StealthMole in every MCP client installed on this machine (Claude Desktop, Claude Code, Cursor, Codex, VS Code, Windsurf, and more):
10
+
11
+ ```bash
12
+ npx -y @stealthmole/mcp install
13
+ ```
14
+
15
+ Pick clients with `-a` (repeatable), e.g. `npx -y @stealthmole/mcp install -a cursor -a codex`. Run `npx -y @stealthmole/mcp install --help` for the full client list. A client that already runs StealthMole is left unchanged, so running it again is safe. Restart your clients afterwards.
16
+
7
17
  ### Claude Desktop
8
18
 
9
19
  Simply run the following command — it will automatically register the MCP server in your Claude Desktop configuration:
@@ -46,7 +46,7 @@ const SERVER_PACKAGE_ARG = /^(?:@stealthmole\/mcp|stealthmole\.mcp)(@.+)?$/;
46
46
  * @param {object} entry an `mcpServers` entry
47
47
  * @returns {boolean}
48
48
  */
49
- function launchesThisServer(entry) {
49
+ export function launchesThisServer(entry) {
50
50
  return Array.isArray(entry?.args) && entry.args.some((arg) => SERVER_PACKAGE_ARG.test(arg));
51
51
  }
52
52
 
package/bin/cli.mjs CHANGED
@@ -15,6 +15,20 @@ const PKG_VERSION = JSON.parse(
15
15
  readFileSync(new URL("../package.json", import.meta.url), "utf-8")
16
16
  ).version;
17
17
 
18
+ // `install` owns everything after it on the command line (-a <agent>, its own
19
+ // --help), so it is routed before the server's flags are read. Loaded lazily:
20
+ // the MCP server launch path never imports add-mcp.
21
+ if (process.argv[2] === "install") {
22
+ const installNodeProblem = unsupportedNodeMessage();
23
+ if (installNodeProblem) {
24
+ // Registering an entry this Node cannot run is worse than not registering.
25
+ console.error(installNodeProblem);
26
+ process.exit(1);
27
+ }
28
+ const { install } = await import("./install.mjs");
29
+ process.exit(await install({ pkgVersion: PKG_VERSION, args: process.argv.slice(3) }));
30
+ }
31
+
18
32
  // --help and --version come first. Both are diagnostics a customer runs to
19
33
  // report what they have installed, so nothing below may run ahead of them:
20
34
  // a fresh registration exits the process, and argv filtering rewrites
@@ -23,6 +37,7 @@ if (process.argv.includes("--help") || process.argv.includes("-h")) {
23
37
  console.error(
24
38
  `@stealthmole/mcp v${PKG_VERSION}\n` +
25
39
  "\nUsage: npx -y @stealthmole/mcp [options]\n" +
40
+ " npx -y @stealthmole/mcp install [-a <agent>]... Register in MCP clients\n" +
26
41
  "\nOptions:\n" +
27
42
  " --port <number> Callback port for OAuth redirect\n" +
28
43
  " --callback-port <number> Alias for --port\n" +
@@ -0,0 +1,94 @@
1
+ // `npx -y @stealthmole/mcp install [-a <agent>]...` — register this server in
2
+ // every MCP client on the machine (Claude Desktop, Claude Code, Cursor, Codex, …)
3
+ // through add-mcp's library, which knows each client's config path and format.
4
+ //
5
+ // chosen agents ──▶ listInstalledServers ──┬─ already launches us ─▶ skip
6
+ // (-a, or all detected) └─ not yet ─────────────▶ upsertServer
7
+ //
8
+ // Skipping is the point: add-mcp's own CLI overwrites a same-named entry (the
9
+ // customer's `env` is lost) and adds a second one under a new name (every tool
10
+ // shows up twice). Identity is the package an entry launches, as in
11
+ // claude-desktop-config.mjs.
12
+
13
+ import {
14
+ detectGlobalAgents,
15
+ getAgentTypes,
16
+ listInstalledServers,
17
+ upsertServer,
18
+ } from "add-mcp";
19
+
20
+ import { SERVER_KEY, launchesThisServer } from "./claude-desktop-config.mjs";
21
+
22
+ const LOG_PREFIX = "[stealthmole-mcp]";
23
+ const AGENT_FLAGS = new Set(["-a", "--agent"]);
24
+
25
+ const USAGE =
26
+ "Usage: npx -y @stealthmole/mcp install [-a <agent>]...\n" +
27
+ "\nRegisters StealthMole MCP in every detected MCP client, or only in the\n" +
28
+ "clients named with -a. Clients that already run it are left unchanged.\n" +
29
+ `\nAgents: ${getAgentTypes().join(", ")}\n`;
30
+
31
+ /**
32
+ * Collect `-a <agent>` / `--agent <agent>` values.
33
+ *
34
+ * @param {string[]} args argv after `install`
35
+ * @returns {string[]}
36
+ */
37
+ export function parseAgents(args) {
38
+ return args.flatMap((arg, i) => (AGENT_FLAGS.has(arg) && args[i + 1] ? [args[i + 1]] : []));
39
+ }
40
+
41
+ /**
42
+ * Register this server in each chosen agent's global config.
43
+ *
44
+ * @param {{pkgVersion: string, args: string[]}} options
45
+ * @returns {Promise<number>} process exit code
46
+ */
47
+ export async function install({ pkgVersion, args }) {
48
+ if (args.includes("--help") || args.includes("-h")) {
49
+ console.error(USAGE);
50
+ return 0;
51
+ }
52
+
53
+ const known = new Set(getAgentTypes());
54
+ const requested = parseAgents(args);
55
+ const unknown = requested.filter((agent) => !known.has(agent));
56
+ if (unknown.length) {
57
+ console.error(`${LOG_PREFIX} Unknown agent: ${unknown.join(", ")}\n\n${USAGE}`);
58
+ return 1;
59
+ }
60
+
61
+ const targets = requested.length ? requested : await detectGlobalAgents();
62
+ if (!targets.length) {
63
+ console.error(`${LOG_PREFIX} No MCP clients found. Name one with -a <agent>.\n\n${USAGE}`);
64
+ return 1;
65
+ }
66
+
67
+ const entry = { command: "npx", args: ["-y", `@stealthmole/mcp@^${pkgVersion}`] };
68
+ let failures = 0;
69
+ let registered = 0;
70
+ for (const agent of await listInstalledServers({ global: true, agents: targets })) {
71
+ const label = `${agent.displayName} (${agent.configPath})`;
72
+ if (agent.error) {
73
+ failures += 1;
74
+ console.error(`${LOG_PREFIX} ✘ ${label}: ${agent.error}`);
75
+ continue;
76
+ }
77
+ const existing = agent.servers.find((server) => launchesThisServer(server.config));
78
+ if (existing) {
79
+ console.error(`${LOG_PREFIX} • ${label}: already registered as "${existing.serverName}"`);
80
+ continue;
81
+ }
82
+ const result = upsertServer(agent.agentType, SERVER_KEY, entry, { local: false });
83
+ if (result.success) {
84
+ registered += 1;
85
+ console.error(`${LOG_PREFIX} ✔ ${label}: registered "${SERVER_KEY}"`);
86
+ } else {
87
+ failures += 1;
88
+ console.error(`${LOG_PREFIX} ✘ ${label}: ${result.error}`);
89
+ }
90
+ }
91
+
92
+ if (registered) console.error(`\n${LOG_PREFIX} Restart your MCP clients to load StealthMole.`);
93
+ return failures ? 1 : 0;
94
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stealthmole/mcp",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "StealthMole Threat Intelligence MCP client - connect Claude Desktop, Cursor, and other MCP clients to StealthMole API",
5
5
  "bin": {
6
6
  "stealthmole-mcp": "bin/cli.mjs"
@@ -12,6 +12,7 @@
12
12
  "bin/"
13
13
  ],
14
14
  "dependencies": {
15
+ "add-mcp": "2.6.1",
15
16
  "mcp-remote": "0.15.1"
16
17
  },
17
18
  "engines": {