@aiwerk/mcp-bridge 2.8.7 → 2.8.9

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.
@@ -3,6 +3,7 @@ import { readFileSync, existsSync, writeFileSync, mkdirSync } from "fs";
3
3
  import { join, dirname, resolve, extname } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import { homedir } from "os";
6
+ import { execFileSync, execSync } from "child_process";
6
7
  import { loadConfig, initConfigDir, warnDeprecatedBundledRecipes, recipeToServerConfig, collectRequiredEnvVars } from "../src/config.js";
7
8
  import { StandaloneServer } from "../src/standalone-server.js";
8
9
  import { PACKAGE_VERSION } from "../src/protocol.js";
@@ -193,34 +194,109 @@ Options:
193
194
  All logs go to stderr. Stdout is reserved for the MCP protocol (stdio mode).
194
195
  `);
195
196
  }
197
+ function whichCmd(name) {
198
+ try {
199
+ execSync(`which ${name}`, { stdio: "ignore" });
200
+ return true;
201
+ }
202
+ catch {
203
+ return false;
204
+ }
205
+ }
196
206
  function cmdInit(logger) {
197
207
  initConfigDir(logger);
198
- // Detect global install: check if we're in a global node_modules (not under a project)
199
- // Global paths: ~/.nvm/.../lib/node_modules, /usr/lib/node_modules, etc.
200
- // Local paths: ~/projects/.../node_modules, ~/node_modules/
201
208
  const isGlobal = __dirname.includes("node_modules") && (__dirname.includes("/lib/node_modules/") || __dirname.includes("\\lib\\node_modules\\"));
202
- const cmd = isGlobal ? "mcp-bridge" : `node ${join(__dirname, "..", "bin", "mcp-bridge.js")}`;
203
- process.stdout.write(`
204
- Next step: add mcp-bridge to your MCP client.
209
+ const bridgeCmd = isGlobal ? "mcp-bridge" : "node";
210
+ const bridgeArgs = isGlobal ? ["serve"] : [join(__dirname, "..", "bin", "mcp-bridge.js"), "serve"];
211
+ // Auto-detect and register with known clients
212
+ let registered = false;
213
+ // Claude Code
214
+ if (whichCmd("claude")) {
215
+ try {
216
+ const addArgs = ["mcp", "add", "-s", "user", "mcp-bridge", "--", bridgeCmd, ...bridgeArgs];
217
+ execFileSync("claude", addArgs, { stdio: "pipe" });
218
+ process.stdout.write("✓ Registered with Claude Code (user scope)\n");
219
+ registered = true;
220
+ }
221
+ catch {
222
+ process.stdout.write("⚠ Claude Code detected but registration failed. Manual setup:\n");
223
+ process.stdout.write(` claude mcp add -s user mcp-bridge -- ${bridgeCmd} ${bridgeArgs.join(" ")}\n\n`);
224
+ }
225
+ }
226
+ // Cursor
227
+ const cursorConfigPath = join(homedir(), ".cursor", "mcp.json");
228
+ if (existsSync(join(homedir(), ".cursor"))) {
229
+ try {
230
+ let cursorConfig = {};
231
+ if (existsSync(cursorConfigPath)) {
232
+ cursorConfig = JSON.parse(readFileSync(cursorConfigPath, "utf-8"));
233
+ }
234
+ if (!cursorConfig.mcpServers)
235
+ cursorConfig.mcpServers = {};
236
+ if (!cursorConfig.mcpServers["mcp-bridge"]) {
237
+ cursorConfig.mcpServers["mcp-bridge"] = { command: bridgeCmd, args: bridgeArgs };
238
+ writeFileSync(cursorConfigPath, JSON.stringify(cursorConfig, null, 2) + "\n", "utf-8");
239
+ process.stdout.write("✓ Registered with Cursor\n");
240
+ registered = true;
241
+ }
242
+ else {
243
+ process.stdout.write("✓ Cursor already configured\n");
244
+ registered = true;
245
+ }
246
+ }
247
+ catch {
248
+ process.stdout.write("⚠ Cursor detected but registration failed.\n");
249
+ }
250
+ }
251
+ // Windsurf
252
+ const windsurfConfigPath = join(homedir(), ".windsurf", "mcp.json");
253
+ if (existsSync(join(homedir(), ".windsurf"))) {
254
+ try {
255
+ let wsConfig = {};
256
+ if (existsSync(windsurfConfigPath)) {
257
+ wsConfig = JSON.parse(readFileSync(windsurfConfigPath, "utf-8"));
258
+ }
259
+ if (!wsConfig.mcpServers)
260
+ wsConfig.mcpServers = {};
261
+ if (!wsConfig.mcpServers["mcp-bridge"]) {
262
+ wsConfig.mcpServers["mcp-bridge"] = { command: bridgeCmd, args: bridgeArgs };
263
+ writeFileSync(windsurfConfigPath, JSON.stringify(wsConfig, null, 2) + "\n", "utf-8");
264
+ process.stdout.write("✓ Registered with Windsurf\n");
265
+ registered = true;
266
+ }
267
+ else {
268
+ process.stdout.write("✓ Windsurf already configured\n");
269
+ registered = true;
270
+ }
271
+ }
272
+ catch {
273
+ process.stdout.write("⚠ Windsurf detected but registration failed.\n");
274
+ }
275
+ }
276
+ if (!registered) {
277
+ const cmd = isGlobal ? "mcp-bridge" : `node ${join(__dirname, "..", "bin", "mcp-bridge.js")}`;
278
+ process.stdout.write(`
279
+ No supported MCP client detected. Add manually:
205
280
 
206
281
  Claude Code: claude mcp add -s user mcp-bridge -- ${cmd} serve
207
282
  Cursor: Add to ~/.cursor/mcp.json
208
283
  Claude Desktop: Add to claude_desktop_config.json
209
- Windsurf: Add to ~/.windsurf/mcp.json
210
284
  OpenClaw: openclaw plugins install @aiwerk/openclaw-mcp-bridge
211
285
 
212
- For Cursor/Claude Desktop/Windsurf, add this JSON block:
286
+ JSON config block (Cursor/Claude Desktop/Windsurf):
213
287
 
214
288
  {
215
289
  "mcp-bridge": {
216
290
  "command": "${isGlobal ? "mcp-bridge" : "node"}",
217
- "args": ${isGlobal ? '["serve"]' : `["${join(__dirname, "..", "bin", "mcp-bridge.js")}", "serve"]`}
291
+ "args": ${JSON.stringify(bridgeArgs)}
218
292
  }
219
293
  }
220
- ${!isGlobal ? "\nTip: Install globally for a cleaner setup:\n npm install -g @aiwerk/mcp-bridge\n" : ""}
221
- After adding, restart your client. The bridge will appear as an 'mcp' tool
222
- with search, install, and catalog actions to discover and add MCP servers.
223
294
  `);
295
+ }
296
+ if (!isGlobal) {
297
+ process.stdout.write("\nTip: Install globally for a cleaner setup:\n npm install -g @aiwerk/mcp-bridge\n");
298
+ }
299
+ process.stdout.write("\nRestart your client. The bridge appears as an 'mcp' tool with\nsearch, install, and catalog actions to discover and add MCP servers.\n");
224
300
  }
225
301
  function cmdCatalog(logger) {
226
302
  const catalogPath = join(PACKAGE_ROOT, "servers", "index.json");
@@ -605,6 +681,11 @@ async function cmdAuth(args, logger) {
605
681
  process.stdout.write(`Authentication successful for ${serverName}. Token stored.\n`);
606
682
  }
607
683
  async function cmdServe(args, logger) {
684
+ // Auto-create config if missing (don't crash on first run)
685
+ const configPath = resolveConfigPath(args.configPath);
686
+ if (!existsSync(configPath)) {
687
+ initConfigDir(logger);
688
+ }
608
689
  let config;
609
690
  try {
610
691
  config = loadConfig({ configPath: args.configPath, logger });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwerk/mcp-bridge",
3
- "version": "2.8.7",
3
+ "version": "2.8.9",
4
4
  "description": "Standalone MCP server that multiplexes multiple MCP servers into one interface",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -44,6 +44,7 @@
44
44
  "build": "tsc",
45
45
  "test": "node --import tsx --test tests/*.test.ts",
46
46
  "typecheck": "tsc --noEmit",
47
+ "postinstall": "node scripts/postinstall.cjs",
47
48
  "prepublishOnly": "tsc && bash scripts/validate-recipes.sh && node -e \"const v=require('./package.json').version;const fs=require('fs');const cl=fs.readFileSync('CHANGELOG.md','utf8');if(!cl.includes('['+v+']')){console.error('ERROR: CHANGELOG.md missing entry for v'+v);process.exit(1)}\"",
48
49
  "validate-recipe": "npx tsx bin/validate-recipe.ts",
49
50
  "lint": "eslint src/",
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Only run auto-init on global installs, not when installed as a dependency
4
+ // npm sets npm_config_global=true for global installs
5
+ const isGlobal = process.env.npm_config_global === "true" ||
6
+ process.env.npm_lifecycle_event === "postinstall" &&
7
+ !process.env.npm_package_name; // not set when we ARE the package being installed as dep
8
+
9
+ // Also check: if our parent is another package's node_modules, skip
10
+ const isNested = __dirname.includes("node_modules") &&
11
+ __dirname.split("node_modules").length > 2;
12
+
13
+ if (isNested) {
14
+ // We're a nested dependency (e.g., inside openclaw-mcp-bridge), skip
15
+ process.exit(0);
16
+ }
17
+
18
+ const { execSync } = require("child_process");
19
+ const { existsSync } = require("fs");
20
+ const { join } = require("path");
21
+ const os = require("os");
22
+
23
+ const configDir = join(os.homedir(), ".mcp-bridge");
24
+ const configPath = join(configDir, "config.json");
25
+
26
+ // Only auto-init if config doesn't exist yet (fresh install)
27
+ if (existsSync(configPath)) {
28
+ console.log("[mcp-bridge] Config already exists, skipping auto-init.");
29
+ process.exit(0);
30
+ }
31
+
32
+ try {
33
+ // Find our own binary
34
+ const binPath = join(__dirname, "..", "dist", "bin", "mcp-bridge.js");
35
+ if (!existsSync(binPath)) {
36
+ // Not built yet (source install), skip
37
+ process.exit(0);
38
+ }
39
+
40
+ console.log("[mcp-bridge] Running auto-init...");
41
+ execSync(`node "${binPath}" init`, { stdio: "inherit" });
42
+ } catch (err) {
43
+ // Don't fail the install if init fails
44
+ console.log("[mcp-bridge] Auto-init skipped (non-fatal):", err.message || err);
45
+ }