@aiwerk/mcp-bridge 2.8.31 → 2.8.33

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.
@@ -593,6 +593,41 @@ async function cmdInstall(serverName, args, logger) {
593
593
  }
594
594
  else {
595
595
  process.stdout.write(`All required environment variables are set. Ready to use.\n`);
596
+ // Pre-discover tools and cache them (so direct mode has tools on first start)
597
+ process.stdout.write(`\nDiscovering tools...\n`);
598
+ try {
599
+ const { StdioTransport } = await import("../src/transport-stdio.js");
600
+ const { SseTransport } = await import("../src/transport-sse.js");
601
+ const { StreamableHttpTransport } = await import("../src/transport-streamable-http.js");
602
+ const { fetchToolsList, initializeProtocol, PACKAGE_VERSION } = await import("../src/protocol.js");
603
+ let transport;
604
+ if (serverConfig.transport === "stdio") {
605
+ transport = new StdioTransport(serverConfig, { servers: {} }, logger, async () => { });
606
+ }
607
+ else if (serverConfig.transport === "sse") {
608
+ transport = new SseTransport(serverConfig, { servers: {} }, logger, async () => { });
609
+ }
610
+ else if (serverConfig.transport === "streamable-http") {
611
+ transport = new StreamableHttpTransport(serverConfig, { servers: {} }, logger, async () => { });
612
+ }
613
+ if (transport) {
614
+ await transport.connect();
615
+ await initializeProtocol(transport, PACKAGE_VERSION);
616
+ const tools = await fetchToolsList(transport);
617
+ // Save to cache
618
+ const cacheToolDir = join(configDir, "cache");
619
+ mkdirSync(cacheToolDir, { recursive: true });
620
+ writeFileSync(join(cacheToolDir, `${serverName}-tools.json`), JSON.stringify(tools, null, 2), "utf-8");
621
+ process.stdout.write(`✓ Cached ${tools.length} tools from "${serverName}"\n`);
622
+ // Disconnect
623
+ await transport.disconnect().catch(() => { });
624
+ }
625
+ }
626
+ catch (discErr) {
627
+ // Non-fatal: tool discovery is a nice-to-have, server still installed
628
+ logger.info(`Tool discovery skipped: ${discErr instanceof Error ? discErr.message : String(discErr)}`);
629
+ process.stdout.write(`Tool discovery skipped (server will discover tools on first use).\n`);
630
+ }
596
631
  }
597
632
  }
598
633
  async function cmdUpdate(logger, checkOnly) {
@@ -260,9 +260,10 @@ export class StandaloneServer {
260
260
  }));
261
261
  return { jsonrpc: "2.0", id, result: { tools } };
262
262
  }
263
- // Lazy: try cache first, then placeholder
263
+ // Lazy: try cache first, add discover tool for uncached servers
264
264
  const lazyTools = [];
265
265
  const globalNames = new Set();
266
+ const uncachedServers = [];
266
267
  for (const [serverName, serverConfig] of Object.entries(this.config.servers)) {
267
268
  const cached = this.loadToolCache(serverName);
268
269
  if (cached && cached.length > 0) {
@@ -273,7 +274,6 @@ export class StandaloneServer {
273
274
  localNames.add(registeredName);
274
275
  globalNames.add(registeredName);
275
276
  lazyTools.push({ name: registeredName, description: tool.description, inputSchema: tool.inputSchema });
276
- // Also populate directTools so call works with lazy connect
277
277
  this.directTools.push({
278
278
  serverName, originalName: tool.name, registeredName,
279
279
  description: tool.description, inputSchema: tool.inputSchema
@@ -281,21 +281,31 @@ export class StandaloneServer {
281
281
  }
282
282
  }
283
283
  else {
284
- // No cache: single placeholder per server
285
- const desc = serverConfig.description || serverName;
286
- const registeredName = pickRegisteredToolName(serverName, "call", this.config.toolPrefix, new Set(), globalNames, this.logger);
287
- globalNames.add(registeredName);
288
- lazyTools.push({
289
- name: registeredName,
290
- description: `[${serverName}] ${desc} — call this tool to discover all available tools from this server.`,
291
- inputSchema: { type: "object", properties: { _discover: { type: "boolean", description: "Set to true to discover all tools" } } }
292
- });
293
- this.directTools.push({
294
- serverName, originalName: "call", registeredName,
295
- description: `[${serverName}] ${desc}`, inputSchema: {}
296
- });
284
+ uncachedServers.push(serverName);
297
285
  }
298
286
  }
287
+ // Add a single discover tool if there are uncached servers
288
+ if (uncachedServers.length > 0) {
289
+ const serverDescs = uncachedServers.map(name => {
290
+ const desc = this.config.servers[name]?.description || name;
291
+ return `${name} (${desc})`;
292
+ });
293
+ lazyTools.push({
294
+ name: "mcp_discover",
295
+ description: `Connect to MCP servers and discover their tools. Servers not yet connected: ${serverDescs.join(", ")}. Call this before using any of these servers.`,
296
+ inputSchema: {
297
+ type: "object",
298
+ properties: {
299
+ server: {
300
+ type: "string",
301
+ description: `Server to discover. Available: ${uncachedServers.join(", ")}`,
302
+ enum: uncachedServers
303
+ }
304
+ },
305
+ required: ["server"]
306
+ }
307
+ });
308
+ }
299
309
  return { jsonrpc: "2.0", id, result: { tools: lazyTools } };
300
310
  }
301
311
  async handleToolsCall(id, params) {
@@ -339,29 +349,34 @@ export class StandaloneServer {
339
349
  }
340
350
  };
341
351
  }
352
+ // Handle mcp_discover tool
353
+ if (toolName === "mcp_discover") {
354
+ const serverName = toolArgs?.server;
355
+ if (!serverName || !this.config.servers[serverName]) {
356
+ const available = Object.keys(this.config.servers).join(", ");
357
+ return {
358
+ jsonrpc: "2.0", id,
359
+ result: { content: [{ type: "text", text: `Please specify a server. Available: ${available}` }] }
360
+ };
361
+ }
362
+ this.logger.info(`[mcp-bridge] Discovering server: ${serverName}`);
363
+ await this.discoverSingleServer(serverName);
364
+ const serverTools = this.directTools.filter(t => t.serverName === serverName);
365
+ const discovered = serverTools.map(t => `${t.registeredName}: ${t.description}`);
366
+ return {
367
+ jsonrpc: "2.0", id,
368
+ result: { content: [{ type: "text", text: `Connected to "${serverName}". Discovered ${serverTools.length} tools:\n\n${discovered.join("\n")}\n\nThese tools are now available. Call them directly by name.` }] }
369
+ };
370
+ }
342
371
  // Direct mode: find and call the tool
343
372
  let entry = this.directTools.find(t => t.registeredName === toolName);
344
- // Lazy discovery: if tool not found, discover only the relevant server (not all)
373
+ // Lazy discovery: if tool not found, try to discover the relevant server
345
374
  if (!entry) {
346
- // Extract server name from tool name (prefix_toolname pattern)
347
375
  const serverName = this.guessServerFromToolName(toolName);
348
- if (serverName) {
349
- this.logger.info(`[mcp-bridge] Lazy discovery for server: ${serverName} (triggered by ${toolName})`);
376
+ if (serverName && !this.directConnections.get(serverName)?.initialized) {
377
+ this.logger.info(`[mcp-bridge] Auto-discovering server: ${serverName} (triggered by ${toolName})`);
350
378
  await this.discoverSingleServer(serverName);
351
- // After discovery, try to find the real tool
352
379
  entry = this.directTools.find(t => t.registeredName === toolName);
353
- if (!entry) {
354
- // Placeholder was called — return discovered tools list and notify client to refresh
355
- const serverTools = this.directTools.filter(t => t.serverName === serverName);
356
- const discovered = serverTools.map(t => `${t.registeredName}: ${t.description}`);
357
- return {
358
- jsonrpc: "2.0",
359
- id,
360
- result: {
361
- content: [{ type: "text", text: `Server "${serverName}" is now connected with ${serverTools.length} tools. The tool list has been updated — please retry your request. Available tools:\n\n${discovered.join("\n")}` }]
362
- }
363
- };
364
- }
365
380
  }
366
381
  }
367
382
  if (!entry) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwerk/mcp-bridge",
3
- "version": "2.8.31",
3
+ "version": "2.8.33",
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",