@aiwerk/mcp-bridge 2.8.23 → 2.8.25

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
@@ -8,7 +8,7 @@
8
8
 
9
9
  🌐 **[aiwerkmcp.com](https://aiwerkmcp.com)** — Learn more about the AIWerk MCP Platform
10
10
 
11
- Works with **Claude Desktop**, **Cursor**, **Windsurf**, **Cline**, **OpenClaw**, or any MCP client.
11
+ Works with **Claude Code**, **Codex (OpenAI)**, **Claude Desktop**, **Cursor**, **Windsurf**, **Cline**, **OpenClaw**, or any MCP client.
12
12
 
13
13
  ## Why?
14
14
 
@@ -39,8 +39,8 @@ npm install -g @aiwerk/mcp-bridge
39
39
  ## Quick Start
40
40
 
41
41
  ```bash
42
- # 1. Initialize config
43
- mcp-bridge init
42
+ # 1. Initialize config and register with Claude Code
43
+ mcp-bridge init --register claude-code
44
44
 
45
45
  # 2. Install a server from the catalog
46
46
  mcp-bridge install todoist
@@ -48,8 +48,7 @@ mcp-bridge install todoist
48
48
  # 3. Add your API key
49
49
  echo "TODOIST_API_TOKEN=your-token" >> ~/.mcp-bridge/.env
50
50
 
51
- # 4. Start (stdio modeconnects to any MCP client)
52
- mcp-bridge
51
+ # 4. Restart Claude Codebridge is ready
53
52
  ```
54
53
 
55
54
  ## Use with Claude Desktop
@@ -418,9 +417,24 @@ Returns promoted tools (sorted by frequency) and full usage stats. All tracking
418
417
  | Mode | Tools exposed | Best for |
419
418
  |------|--------------|----------|
420
419
  | `router` (default) | Single `mcp` meta-tool | 3+ servers, token-conscious agents |
421
- | `direct` | All tools individually | Few servers, simple agents |
420
+ | `direct` | All tools individually | Clients with deferred/lazy tool loading (Claude Code), few servers |
422
421
 
423
- **Router mode** the agent calls `mcp(server="todoist", action="list")` to discover, then `mcp(server="todoist", tool="find-tasks", params={...})` to execute.
422
+ Switch modes via CLI or config:
423
+
424
+ ```bash
425
+ mcp-bridge init --mode direct # all tools exposed individually
426
+ mcp-bridge init --mode router # single mcp meta-tool (default)
427
+ ```
428
+
429
+ Or set in `~/.mcp-bridge/config.json`:
430
+
431
+ ```json
432
+ { "mode": "direct" }
433
+ ```
434
+
435
+ **Router mode** — the agent calls `mcp(server="todoist", action="list")` to discover, then `mcp(server="todoist", tool="find-tasks", params={...})` to execute. Best when you have many servers and want minimal token usage.
436
+
437
+ **Direct mode** — all tools from all servers are registered individually as `todoist_find_tasks`, `github_list_repos`, etc. The bridge still provides unified config, catalog install, OAuth2, security, retries, and reconnection. Ideal for clients that support deferred/lazy tool loading, where tools are registered but not loaded into context until needed.
424
438
 
425
439
  ### Multi-Server Tool Resolution
426
440
 
@@ -558,12 +572,16 @@ mcp-bridge # Start in stdio mode (default)
558
572
  mcp-bridge --sse --port 3000 # Start as SSE server
559
573
  mcp-bridge --http --port 3000 # Start as HTTP server
560
574
  mcp-bridge --verbose # Info-level logs to stderr
561
- mcp-bridge --debug # Full protocol logs to stderr
575
+ mcp-bridge --debug # Full debug metadata in tool responses
562
576
  mcp-bridge --config ./my.json # Custom config file
563
577
 
564
- mcp-bridge init # Create ~/.mcp-bridge/ with template
565
- mcp-bridge install <server> # Install from catalog
566
- mcp-bridge catalog # List available servers
578
+ mcp-bridge init # Create ~/.mcp-bridge/ with template config
579
+ mcp-bridge init --register claude-code # Init + register with Claude Code
580
+ mcp-bridge init --register codex # Init + register with Codex
581
+ mcp-bridge init --register cursor # Init + register with Cursor
582
+ mcp-bridge init --register windsurf # Init + register with Windsurf
583
+ mcp-bridge install <server> # Install from online catalog
584
+ mcp-bridge catalog # Browse 100+ available servers
567
585
  mcp-bridge servers # List configured servers
568
586
  mcp-bridge search <query> # Search catalog by keyword
569
587
  mcp-bridge update [--check] # Check for / install updates
@@ -574,33 +592,36 @@ mcp-bridge auth logout <server> # Remove stored token
574
592
  mcp-bridge auth status # Show auth status for all servers
575
593
  ```
576
594
 
595
+ ## Agent Integration
596
+
597
+ When connected to an MCP client (Claude Code, Codex, Cursor, etc.), the bridge exposes a single `mcp` meta-tool. Agents can discover and install servers at runtime:
598
+
599
+ ```
600
+ mcp(action="search", params={query: "task management"}) # Search catalog
601
+ mcp(action="install", params={name: "todoist"}) # Install server (persisted to config)
602
+ mcp(action="catalog") # Browse all servers
603
+ mcp(action="list", server="todoist") # Discover tools on a server
604
+ mcp(action="call", server="todoist", tool="find-tasks", params={query: "today"})
605
+ ```
606
+
607
+ The tool description automatically includes all connected servers with their descriptions, so agents know which server to use for what. New servers installed via the bridge are persisted to `~/.mcp-bridge/config.json` and survive restarts.
608
+
577
609
  ## Server Catalog
578
610
 
579
- Built-in catalog with pre-configured servers:
580
-
581
- | Server | Transport | Description |
582
- |--------|-----------|-------------|
583
- | todoist | stdio | Task management |
584
- | github | stdio | Repos, issues, PRs |
585
- | notion | stdio | Pages and databases |
586
- | stripe | stdio | Payments and billing |
587
- | linear | stdio | Project management |
588
- | google-maps | stdio | Places, geocoding, directions |
589
- | hetzner | stdio | Cloud infrastructure |
590
- | miro | stdio | Collaborative whiteboard |
591
- | wise | stdio | International payments |
592
- | tavily | stdio | AI-optimized web search |
593
- | apify | streamable-http | Web scraping and automation |
594
- | atlassian | stdio | Confluence and Jira |
595
- | chrome-devtools | stdio | Chrome browser automation |
596
- | hostinger | sse | Web hosting management |
611
+ Browse and install from the [AIWerk MCP Catalog](https://catalog.aiwerk.ch) with 100+ verified, signed recipes:
597
612
 
598
613
  ```bash
599
- mcp-bridge install todoist # Interactive setup with API key prompt
600
- mcp-bridge catalog # Full list
601
- mcp-bridge search payments # Search by keyword
614
+ mcp-bridge catalog # Browse all 100+ servers
615
+ mcp-bridge search payments # Search by keyword
616
+ mcp-bridge install todoist # Install from catalog
602
617
  ```
603
618
 
619
+ Popular servers include: todoist, github, notion, stripe, linear, google-maps, slack, supabase, mongodb, playwright, docker, and many more.
620
+
621
+ All catalog recipes are Ed25519 signed and security-audited. The bridge verifies signatures before installation.
622
+
623
+ > **Note**: The bundled `servers/` directory is deprecated. All servers now come from the online catalog.
624
+
604
625
  ## Library Usage
605
626
 
606
627
  Use as a dependency in your own MCP server or OpenClaw plugin:
@@ -667,11 +688,14 @@ For production deployments with high security requirements, consider adding an e
667
688
  | ✅ | OAuth2 Client Credentials | 2.1.0 |
668
689
  | ✅ | OAuth2 Authorization Code + PKCE | 2.5.0 |
669
690
  | ✅ | OAuth2 Device Code flow (headless) | 2.6.0 |
670
- | 🔜 | Auto-discovery (zero-config server registration) | planned |
691
+ | | Agent-driven discovery (search/install at runtime) | 2.8.6 |
671
692
  | 🔜 | Hosted bridge (bridge.aiwerk.ch) | planned |
672
693
  | ✅ | Remote catalog integration | 2.8.0 |
694
+ | ✅ | CLI online catalog | 2.8.23 |
695
+ | ✅ | Debug mode (_debug metadata) | 2.8.4 |
673
696
  | 🔜 | OpenTelemetry / Prometheus metrics | planned |
674
697
  | 🔜 | PII redaction | planned |
698
+ | 🔜 | Skill system (recipe.json skills for agents) | planned |
675
699
 
676
700
  See [docs/hosted-bridge-spec.md](docs/hosted-bridge-spec.md) for the hosted bridge architecture.
677
701
 
@@ -106,6 +106,14 @@ function parseArgs(argv) {
106
106
  }
107
107
  args.register = argv[i];
108
108
  break;
109
+ case "--mode":
110
+ i++;
111
+ if (argv[i] !== "router" && argv[i] !== "direct") {
112
+ process.stderr.write("Error: --mode must be 'router' or 'direct'\n");
113
+ process.exit(1);
114
+ }
115
+ args.mode = argv[i];
116
+ break;
109
117
  case "--daily":
110
118
  i++;
111
119
  args.daily = parseInt(argv[i], 10);
@@ -179,7 +187,7 @@ Usage:
179
187
  mcp-bridge Start in stdio mode (default)
180
188
  mcp-bridge --sse --port 3000 Start as SSE server
181
189
  mcp-bridge --http --port 3000 Start as streamable-http server
182
- mcp-bridge init [--register <client>] Create config + optionally register with a client
190
+ mcp-bridge init [--register <client>] [--mode router|direct] Create config + optionally register
183
191
  mcp-bridge install <server> Install a server from the catalog
184
192
  mcp-bridge catalog [--offline] List available servers
185
193
  mcp-bridge servers List configured servers
@@ -211,8 +219,24 @@ function whichCmd(name) {
211
219
  return false;
212
220
  }
213
221
  }
214
- function cmdInit(logger, register) {
222
+ function cmdInit(logger, register, mode) {
215
223
  initConfigDir(logger);
224
+ // Apply --mode to config
225
+ if (mode) {
226
+ const configPath = join(homedir(), ".mcp-bridge", "config.json");
227
+ if (existsSync(configPath)) {
228
+ try {
229
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
230
+ if (raw.mode !== mode) {
231
+ raw.mode = mode;
232
+ writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n", "utf-8");
233
+ process.stdout.write(`Mode set to "${mode}" in ${configPath}\n`);
234
+ }
235
+ }
236
+ catch { /* ignore parse errors */ }
237
+ }
238
+ }
239
+ // If --register is specified, do both mode + register
216
240
  const isGlobal = __dirname.includes("node_modules") && (__dirname.includes("/lib/node_modules/") || __dirname.includes("\\lib\\node_modules\\"));
217
241
  const bridgeCmd = isGlobal ? "mcp-bridge" : "node";
218
242
  const bridgeArgs = isGlobal ? ["serve"] : [join(__dirname, "..", "bin", "mcp-bridge.js"), "serve"];
@@ -766,7 +790,7 @@ async function main() {
766
790
  printHelp();
767
791
  break;
768
792
  case "init":
769
- cmdInit(logger, args.register);
793
+ cmdInit(logger, args.register, args.mode);
770
794
  break;
771
795
  case "catalog":
772
796
  await cmdCatalog(logger, args.offline);
@@ -42,6 +42,7 @@ export interface CatalogRecipe {
42
42
  type: string;
43
43
  required?: boolean;
44
44
  envVars?: string[];
45
+ credentialsUrl?: string;
45
46
  };
46
47
  [key: string]: unknown;
47
48
  }
@@ -110,6 +110,11 @@ export type RouterDispatchResponse = {
110
110
  message: string;
111
111
  missingEnvVars?: string[];
112
112
  credentialsUrl?: string;
113
+ } | {
114
+ action: "set-mode";
115
+ mode: string;
116
+ message: string;
117
+ requiresRestart?: boolean;
113
118
  } | {
114
119
  action: "intent";
115
120
  intent: string;
@@ -77,7 +77,7 @@ export class McpRouter {
77
77
  return desc ? `${name} (${desc})` : name;
78
78
  })
79
79
  .join(", ");
80
- return `MCP server multiplexer with ${serverNames.length} connected servers: ${serverList}. Actions: 'call' to execute a tool, 'list' to discover tools on a server, 'batch' for multiple calls in one round-trip, 'status' to check connections, 'refresh' to re-discover tools. To add new MCP servers, always use this tool first (not npm install): 'search' to find servers in the verified catalog (100+ signed, security-audited recipes), 'install' to add a server by name. If the user mentions a specific tool by name, the call action auto-connects and works without listing first.`;
80
+ return `MCP server multiplexer with ${serverNames.length} connected servers: ${serverList}. Actions: 'call' to execute a tool, 'list' to discover tools on a server, 'batch' for multiple calls in one round-trip, 'status' to check connections, 'refresh' to re-discover tools. To add new MCP servers, always use this tool first (not npm install): 'search' to find servers in the verified catalog (100+ signed, security-audited recipes), 'install' to add a server by name. Use 'set-mode' with params.mode='direct' to expose all tools individually (requires restart). If the user mentions a specific tool by name, the call action auto-connects and works without listing first.`;
81
81
  }
82
82
  async dispatch(server, action = "call", tool, params) {
83
83
  try {
@@ -165,6 +165,10 @@ export class McpRouter {
165
165
  if (!serverName) {
166
166
  return this.error("invalid_params", "server name is required for action=install (pass as server field or params.name)");
167
167
  }
168
+ // Sanitize serverName: only lowercase alphanumeric + hyphens (matches recipe spec id format)
169
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(serverName)) {
170
+ return this.error("invalid_params", `Invalid server name "${serverName}". Must match /^[a-z0-9][a-z0-9-]*$/ (lowercase alphanumeric + hyphens).`);
171
+ }
168
172
  if (this.servers[serverName]) {
169
173
  return { action: "install", server: serverName, installed: true, message: `Server "${serverName}" is already configured.` };
170
174
  }
@@ -336,8 +340,41 @@ export class McpRouter {
336
340
  return this.error("connection_failed", `Failed to connect to ${server}: ${error instanceof Error ? error.message : String(error)}`);
337
341
  }
338
342
  }
343
+ // Set mode (persists to config, requires bridge restart to take effect)
344
+ if (normalizedAction === "set-mode") {
345
+ const newMode = params?.mode;
346
+ if (newMode !== "router" && newMode !== "direct") {
347
+ return this.error("invalid_params", "mode must be 'router' or 'direct'");
348
+ }
349
+ try {
350
+ const os = await import("os");
351
+ const fs = await import("fs");
352
+ const path = await import("path");
353
+ const configPath = path.join(os.homedir(), ".mcp-bridge", "config.json");
354
+ if (fs.existsSync(configPath)) {
355
+ const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
356
+ const oldMode = raw.mode || "router";
357
+ if (oldMode === newMode) {
358
+ return { action: "set-mode", mode: newMode, message: `Already in "${newMode}" mode.` };
359
+ }
360
+ raw.mode = newMode;
361
+ fs.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n", "utf-8");
362
+ this.logger.info(`Mode changed from "${oldMode}" to "${newMode}" in ${configPath}`);
363
+ return {
364
+ action: "set-mode",
365
+ mode: newMode,
366
+ message: `Mode changed to "${newMode}". Restart the bridge for changes to take effect.`,
367
+ requiresRestart: true,
368
+ };
369
+ }
370
+ return this.error("mcp_error", "Config file not found");
371
+ }
372
+ catch (err) {
373
+ return this.error("mcp_error", `Failed to set mode: ${err instanceof Error ? err.message : String(err)}`);
374
+ }
375
+ }
339
376
  if (normalizedAction !== "call") {
340
- return this.error("invalid_params", `action must be one of: list, call, batch, refresh, schema, intent, status, promotions, search, catalog, install`);
377
+ return this.error("invalid_params", `action must be one of: list, call, batch, refresh, schema, intent, status, promotions, search, catalog, install, set-mode`);
341
378
  }
342
379
  if (!tool) {
343
380
  return this.error("invalid_params", "tool is required for action=call");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwerk/mcp-bridge",
3
- "version": "2.8.23",
3
+ "version": "2.8.25",
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",