@aiwerk/mcp-bridge 2.8.13 → 2.8.15

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.
@@ -98,6 +98,14 @@ function parseArgs(argv) {
98
98
  case "--offline":
99
99
  args.offline = true;
100
100
  break;
101
+ case "--register":
102
+ i++;
103
+ if (!argv[i]) {
104
+ process.stderr.write("Error: --register requires a client name (claude-code, codex, cursor, windsurf)\n");
105
+ process.exit(1);
106
+ }
107
+ args.register = argv[i];
108
+ break;
101
109
  case "--daily":
102
110
  i++;
103
111
  args.daily = parseInt(argv[i], 10);
@@ -171,7 +179,7 @@ Usage:
171
179
  mcp-bridge Start in stdio mode (default)
172
180
  mcp-bridge --sse --port 3000 Start as SSE server
173
181
  mcp-bridge --http --port 3000 Start as streamable-http server
174
- mcp-bridge init Create ~/.mcp-bridge/ with config template
182
+ mcp-bridge init [--register <client>] Create config + optionally register with a client
175
183
  mcp-bridge install <server> Install a server from the catalog
176
184
  mcp-bridge catalog [--offline] List available servers
177
185
  mcp-bridge servers List configured servers
@@ -203,130 +211,123 @@ function whichCmd(name) {
203
211
  return false;
204
212
  }
205
213
  }
206
- function cmdInit(logger) {
214
+ function cmdInit(logger, register) {
207
215
  initConfigDir(logger);
208
216
  const isGlobal = __dirname.includes("node_modules") && (__dirname.includes("/lib/node_modules/") || __dirname.includes("\\lib\\node_modules\\"));
209
217
  const bridgeCmd = isGlobal ? "mcp-bridge" : "node";
210
218
  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) → ~/.claude.json\n Restart Claude Code to activate.\n");
219
- registered = true;
219
+ const cmd = isGlobal ? "mcp-bridge" : `node ${join(__dirname, "..", "bin", "mcp-bridge.js")}`;
220
+ if (register) {
221
+ registerClient(register, bridgeCmd, bridgeArgs, cmd);
222
+ return;
223
+ }
224
+ const detected = [];
225
+ if (whichCmd("claude"))
226
+ detected.push("claude-code");
227
+ if (whichCmd("codex"))
228
+ detected.push("codex");
229
+ if (existsSync(join(homedir(), ".cursor")))
230
+ detected.push("cursor");
231
+ if (existsSync(join(homedir(), ".windsurf")))
232
+ detected.push("windsurf");
233
+ if (detected.length > 0) {
234
+ process.stdout.write("\nDetected MCP clients. Register with:\n");
235
+ for (const client of detected) {
236
+ process.stdout.write(` mcp-bridge init --register ${client}\n`);
220
237
  }
221
- catch (err) {
222
- const msg = err instanceof Error ? err.message : String(err);
223
- if (msg.includes("already exists")) {
224
- registered = true;
225
- // Silent - already configured, no need to print
238
+ }
239
+ else {
240
+ process.stdout.write(`\nNo supported MCP client detected. Manual setup:\n\n Claude Code: claude mcp add -s user mcp-bridge -- ${cmd} serve\n Codex: codex mcp add mcp-bridge -- ${cmd} serve\n Cursor: Add to ~/.cursor/mcp.json\n Claude Desktop: Add to claude_desktop_config.json\n OpenClaw: openclaw plugins install @aiwerk/openclaw-mcp-bridge\n\nJSON config block (Cursor/Claude Desktop/Windsurf):\n\n {\n "mcp-bridge": {\n "command": "${isGlobal ? "mcp-bridge" : "node"}",\n "args": ${JSON.stringify(bridgeArgs)}\n }\n }\n`);
241
+ }
242
+ if (!isGlobal) {
243
+ process.stdout.write("\nTip: Install globally for a cleaner setup:\n npm install -g @aiwerk/mcp-bridge\n");
244
+ }
245
+ }
246
+ function registerClient(client, bridgeCmd, bridgeArgs, cmd) {
247
+ switch (client) {
248
+ case "claude-code": {
249
+ try {
250
+ execFileSync("claude", ["mcp", "add", "-s", "user", "mcp-bridge", "--", bridgeCmd, ...bridgeArgs], { stdio: "pipe" });
251
+ process.stdout.write("✓ Registered with Claude Code → ~/.claude.json\n Restart Claude Code to activate.\n");
226
252
  }
227
- else {
228
- process.stdout.write("⚠ Claude Code detected but registration failed. Manual setup:\n");
229
- process.stdout.write(` claude mcp add -s user mcp-bridge -- ${bridgeCmd} ${bridgeArgs.join(" ")}\n\n`);
253
+ catch (err) {
254
+ const msg = err instanceof Error ? err.message : String(err);
255
+ if (msg.includes("already exists")) {
256
+ process.stdout.write("✓ Claude Code already configured.\n");
257
+ }
258
+ else {
259
+ process.stdout.write(`⚠ Registration failed. Manual setup:\n claude mcp add -s user mcp-bridge -- ${cmd} serve\n`);
260
+ }
230
261
  }
262
+ break;
231
263
  }
232
- }
233
- // Cursor
234
- const cursorConfigPath = join(homedir(), ".cursor", "mcp.json");
235
- if (existsSync(join(homedir(), ".cursor"))) {
236
- try {
237
- let cursorConfig = {};
238
- if (existsSync(cursorConfigPath)) {
239
- cursorConfig = JSON.parse(readFileSync(cursorConfigPath, "utf-8"));
264
+ case "codex": {
265
+ const codexConfigPath = join(homedir(), ".codex", "config.toml");
266
+ try {
267
+ const codexConfig = existsSync(codexConfigPath) ? readFileSync(codexConfigPath, "utf-8") : "";
268
+ if (codexConfig.includes("[mcp_servers.mcp-bridge]") || codexConfig.includes("[mcp_servers.aiwerk-bridge]")) {
269
+ process.stdout.write("✓ Codex already configured.\n");
270
+ }
271
+ else {
272
+ execFileSync("codex", ["mcp", "add", "mcp-bridge", "--", bridgeCmd, ...bridgeArgs], { stdio: "pipe" });
273
+ process.stdout.write(`✓ Registered with Codex → ${codexConfigPath}\n Restart Codex to activate.\n`);
274
+ }
240
275
  }
241
- if (!cursorConfig.mcpServers)
242
- cursorConfig.mcpServers = {};
243
- if (!cursorConfig.mcpServers["mcp-bridge"]) {
244
- cursorConfig.mcpServers["mcp-bridge"] = { command: bridgeCmd, args: bridgeArgs };
245
- writeFileSync(cursorConfigPath, JSON.stringify(cursorConfig, null, 2) + "\n", "utf-8");
246
- process.stdout.write(`✓ Registered with Cursor → ${cursorConfigPath}\n Restart Cursor to activate.\n`);
247
- registered = true;
276
+ catch {
277
+ process.stdout.write(`⚠ Registration failed. Manual setup:\n codex mcp add mcp-bridge -- ${cmd} serve\n`);
248
278
  }
249
- else {
250
- registered = true;
251
- // Silent - already configured
252
- registered = true;
253
- }
254
- }
255
- catch {
256
- process.stdout.write("⚠ Cursor detected but registration failed.\n");
279
+ break;
257
280
  }
258
- }
259
- // Windsurf
260
- const windsurfConfigPath = join(homedir(), ".windsurf", "mcp.json");
261
- if (existsSync(join(homedir(), ".windsurf"))) {
262
- try {
263
- let wsConfig = {};
264
- if (existsSync(windsurfConfigPath)) {
265
- wsConfig = JSON.parse(readFileSync(windsurfConfigPath, "utf-8"));
266
- }
267
- if (!wsConfig.mcpServers)
268
- wsConfig.mcpServers = {};
269
- if (!wsConfig.mcpServers["mcp-bridge"]) {
270
- wsConfig.mcpServers["mcp-bridge"] = { command: bridgeCmd, args: bridgeArgs };
271
- writeFileSync(windsurfConfigPath, JSON.stringify(wsConfig, null, 2) + "\n", "utf-8");
272
- process.stdout.write(`✓ Registered with Windsurf → ${windsurfConfigPath}\n Restart Windsurf to activate.\n`);
273
- registered = true;
281
+ case "cursor": {
282
+ const configPath = join(homedir(), ".cursor", "mcp.json");
283
+ try {
284
+ let config = {};
285
+ if (existsSync(configPath))
286
+ config = JSON.parse(readFileSync(configPath, "utf-8"));
287
+ if (!config.mcpServers)
288
+ config.mcpServers = {};
289
+ if (config.mcpServers["mcp-bridge"]) {
290
+ process.stdout.write("✓ Cursor already configured.\n");
291
+ }
292
+ else {
293
+ config.mcpServers["mcp-bridge"] = { command: bridgeCmd, args: bridgeArgs };
294
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
295
+ process.stdout.write(`✓ Registered with Cursor → ${configPath}\n Restart Cursor to activate.\n`);
296
+ }
274
297
  }
275
- else {
276
- registered = true;
277
- // Silent - already configured
278
- registered = true;
298
+ catch {
299
+ process.stdout.write("⚠ Cursor registration failed.\n");
279
300
  }
301
+ break;
280
302
  }
281
- catch {
282
- process.stdout.write("⚠ Windsurf detected but registration failed.\n");
283
- }
284
- }
285
- // Codex (OpenAI)
286
- if (whichCmd("codex")) {
287
- const codexConfigPath = join(homedir(), ".codex", "config.toml");
288
- try {
289
- const codexConfig = existsSync(codexConfigPath) ? readFileSync(codexConfigPath, "utf-8") : "";
290
- if (codexConfig.includes("[mcp_servers.mcp-bridge]") || codexConfig.includes("[mcp_servers.aiwerk-bridge]")) {
291
- registered = true;
292
- // Silent - already configured
303
+ case "windsurf": {
304
+ const configPath = join(homedir(), ".windsurf", "mcp.json");
305
+ try {
306
+ let config = {};
307
+ if (existsSync(configPath))
308
+ config = JSON.parse(readFileSync(configPath, "utf-8"));
309
+ if (!config.mcpServers)
310
+ config.mcpServers = {};
311
+ if (config.mcpServers["mcp-bridge"]) {
312
+ process.stdout.write(" Windsurf already configured.\n");
313
+ }
314
+ else {
315
+ config.mcpServers["mcp-bridge"] = { command: bridgeCmd, args: bridgeArgs };
316
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
317
+ process.stdout.write(`✓ Registered with Windsurf → ${configPath}\n Restart Windsurf to activate.\n`);
318
+ }
293
319
  }
294
- else {
295
- execFileSync("codex", ["mcp", "add", "mcp-bridge", "--", bridgeCmd, ...bridgeArgs], { stdio: "pipe" });
296
- process.stdout.write(`✓ Registered with Codex → ${codexConfigPath}\n Restart Codex to activate.\n`);
297
- registered = true;
320
+ catch {
321
+ process.stdout.write(" Windsurf registration failed.\n");
298
322
  }
323
+ break;
299
324
  }
300
- catch {
301
- process.stdout.write("⚠ Codex detected but registration failed. Manual setup:\n");
302
- process.stdout.write(` codex mcp add mcp-bridge -- ${bridgeCmd} ${bridgeArgs.join(" ")}\n\n`);
325
+ default: {
326
+ const isGlob = bridgeCmd === "mcp-bridge";
327
+ process.stdout.write(`Unknown client "${client}". Generic MCP config (add to your client's MCP settings):\n\n {\n "mcp-bridge": {\n "command": "${isGlob ? "mcp-bridge" : "node"}",\n "args": ${JSON.stringify(bridgeArgs)}\n }\n }\n\nKnown clients with auto-registration: claude-code, codex, cursor, windsurf\n`);
328
+ break;
303
329
  }
304
330
  }
305
- if (!registered) {
306
- const cmd = isGlobal ? "mcp-bridge" : `node ${join(__dirname, "..", "bin", "mcp-bridge.js")}`;
307
- process.stdout.write(`
308
- No supported MCP client detected. Add manually:
309
-
310
- Claude Code: claude mcp add -s user mcp-bridge -- ${cmd} serve
311
- Codex: codex mcp add mcp-bridge -- ${cmd} serve
312
- Cursor: Add to ~/.cursor/mcp.json
313
- Claude Desktop: Add to claude_desktop_config.json
314
- OpenClaw: openclaw plugins install @aiwerk/openclaw-mcp-bridge
315
-
316
- JSON config block (Cursor/Claude Desktop/Windsurf):
317
-
318
- {
319
- "mcp-bridge": {
320
- "command": "${isGlobal ? "mcp-bridge" : "node"}",
321
- "args": ${JSON.stringify(bridgeArgs)}
322
- }
323
- }
324
- `);
325
- }
326
- if (!isGlobal) {
327
- process.stdout.write("\nTip: Install globally for a cleaner setup:\n npm install -g @aiwerk/mcp-bridge\n");
328
- }
329
- 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");
330
331
  }
331
332
  function cmdCatalog(logger) {
332
333
  const catalogPath = join(PACKAGE_ROOT, "servers", "index.json");
@@ -763,7 +764,7 @@ async function main() {
763
764
  printHelp();
764
765
  break;
765
766
  case "init":
766
- cmdInit(logger);
767
+ cmdInit(logger, args.register);
767
768
  break;
768
769
  case "catalog":
769
770
  cmdCatalog(logger);
@@ -108,11 +108,13 @@ export class McpRouter {
108
108
  return this.error("mcp_error", "Catalog client not available");
109
109
  }
110
110
  try {
111
- const results = await this.catalogClient.search(query);
111
+ const searchResponse = await this.catalogClient.search(query);
112
+ // search() may return array or {results: [...]} depending on catalog API version
113
+ const results = Array.isArray(searchResponse) ? searchResponse : searchResponse.results || [];
112
114
  return {
113
115
  action: "search",
114
116
  query,
115
- results: results.map(r => ({
117
+ results: results.map((r) => ({
116
118
  id: r.name,
117
119
  name: r.name,
118
120
  description: r.description || "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwerk/mcp-bridge",
3
- "version": "2.8.13",
3
+ "version": "2.8.15",
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",