@aiwerk/mcp-bridge 2.8.30 → 2.8.32
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.
|
@@ -14,6 +14,7 @@ export declare class StandaloneServer {
|
|
|
14
14
|
private readonly requestIdState;
|
|
15
15
|
private directTools;
|
|
16
16
|
private directConnections;
|
|
17
|
+
private stdoutRef;
|
|
17
18
|
constructor(config: BridgeConfig, logger: Logger);
|
|
18
19
|
private isRouterMode;
|
|
19
20
|
/** Start stdio mode: read JSON-RPC from stdin, write responses to stdout.
|
|
@@ -30,6 +31,8 @@ export declare class StandaloneServer {
|
|
|
30
31
|
/** Connect to all backend servers and discover their tools (direct mode). */
|
|
31
32
|
private discoverDirectTools;
|
|
32
33
|
private _doDiscovery;
|
|
34
|
+
/** Send notifications/tools/list_changed to the client via stdout */
|
|
35
|
+
private sendToolsChanged;
|
|
33
36
|
/** Extract server name from a tool name like "todoist_call" or "github_call" */
|
|
34
37
|
private guessServerFromToolName;
|
|
35
38
|
/** Discover tools from a single server (lazy, per-server) */
|
|
@@ -26,6 +26,7 @@ export class StandaloneServer {
|
|
|
26
26
|
// Direct mode state
|
|
27
27
|
directTools = [];
|
|
28
28
|
directConnections = new Map();
|
|
29
|
+
stdoutRef = null;
|
|
29
30
|
constructor(config, logger) {
|
|
30
31
|
this.config = config;
|
|
31
32
|
this.logger = logger;
|
|
@@ -50,6 +51,7 @@ export class StandaloneServer {
|
|
|
50
51
|
async startStdio() {
|
|
51
52
|
const stdin = process.stdin;
|
|
52
53
|
const stdout = process.stdout;
|
|
54
|
+
this.stdoutRef = stdout;
|
|
53
55
|
let buffer = Buffer.alloc(0);
|
|
54
56
|
// LSP framing state
|
|
55
57
|
let lspContentLength = -1; // -1 means not in LSP mode for current message
|
|
@@ -258,9 +260,10 @@ export class StandaloneServer {
|
|
|
258
260
|
}));
|
|
259
261
|
return { jsonrpc: "2.0", id, result: { tools } };
|
|
260
262
|
}
|
|
261
|
-
// Lazy: try cache first,
|
|
263
|
+
// Lazy: try cache first, add discover tool for uncached servers
|
|
262
264
|
const lazyTools = [];
|
|
263
265
|
const globalNames = new Set();
|
|
266
|
+
const uncachedServers = [];
|
|
264
267
|
for (const [serverName, serverConfig] of Object.entries(this.config.servers)) {
|
|
265
268
|
const cached = this.loadToolCache(serverName);
|
|
266
269
|
if (cached && cached.length > 0) {
|
|
@@ -271,7 +274,6 @@ export class StandaloneServer {
|
|
|
271
274
|
localNames.add(registeredName);
|
|
272
275
|
globalNames.add(registeredName);
|
|
273
276
|
lazyTools.push({ name: registeredName, description: tool.description, inputSchema: tool.inputSchema });
|
|
274
|
-
// Also populate directTools so call works with lazy connect
|
|
275
277
|
this.directTools.push({
|
|
276
278
|
serverName, originalName: tool.name, registeredName,
|
|
277
279
|
description: tool.description, inputSchema: tool.inputSchema
|
|
@@ -279,21 +281,31 @@ export class StandaloneServer {
|
|
|
279
281
|
}
|
|
280
282
|
}
|
|
281
283
|
else {
|
|
282
|
-
|
|
283
|
-
const desc = serverConfig.description || serverName;
|
|
284
|
-
const registeredName = pickRegisteredToolName(serverName, "call", this.config.toolPrefix, new Set(), globalNames, this.logger);
|
|
285
|
-
globalNames.add(registeredName);
|
|
286
|
-
lazyTools.push({
|
|
287
|
-
name: registeredName,
|
|
288
|
-
description: `[${serverName}] ${desc} — call this tool to discover all available tools from this server.`,
|
|
289
|
-
inputSchema: { type: "object", properties: { _discover: { type: "boolean", description: "Set to true to discover all tools" } } }
|
|
290
|
-
});
|
|
291
|
-
this.directTools.push({
|
|
292
|
-
serverName, originalName: "call", registeredName,
|
|
293
|
-
description: `[${serverName}] ${desc}`, inputSchema: {}
|
|
294
|
-
});
|
|
284
|
+
uncachedServers.push(serverName);
|
|
295
285
|
}
|
|
296
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
|
+
}
|
|
297
309
|
return { jsonrpc: "2.0", id, result: { tools: lazyTools } };
|
|
298
310
|
}
|
|
299
311
|
async handleToolsCall(id, params) {
|
|
@@ -337,25 +349,34 @@ export class StandaloneServer {
|
|
|
337
349
|
}
|
|
338
350
|
};
|
|
339
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
|
+
}
|
|
340
371
|
// Direct mode: find and call the tool
|
|
341
372
|
let entry = this.directTools.find(t => t.registeredName === toolName);
|
|
342
|
-
// Lazy discovery: if tool not found, discover
|
|
373
|
+
// Lazy discovery: if tool not found, try to discover the relevant server
|
|
343
374
|
if (!entry) {
|
|
344
|
-
// Extract server name from tool name (prefix_toolname pattern)
|
|
345
375
|
const serverName = this.guessServerFromToolName(toolName);
|
|
346
|
-
if (serverName) {
|
|
347
|
-
this.logger.info(`[mcp-bridge]
|
|
376
|
+
if (serverName && !this.directConnections.get(serverName)?.initialized) {
|
|
377
|
+
this.logger.info(`[mcp-bridge] Auto-discovering server: ${serverName} (triggered by ${toolName})`);
|
|
348
378
|
await this.discoverSingleServer(serverName);
|
|
349
|
-
|
|
350
|
-
const serverTools = this.directTools.filter(t => t.serverName === serverName);
|
|
351
|
-
const discovered = serverTools.map(t => `${t.registeredName}: ${t.description}`);
|
|
352
|
-
return {
|
|
353
|
-
jsonrpc: "2.0",
|
|
354
|
-
id,
|
|
355
|
-
result: {
|
|
356
|
-
content: [{ type: "text", text: `Discovered ${serverTools.length} tools from ${serverName}. Available tools:\n\n${discovered.join("\n")}\n\nCall any of these tools directly by name.` }]
|
|
357
|
-
}
|
|
358
|
-
};
|
|
379
|
+
entry = this.directTools.find(t => t.registeredName === toolName);
|
|
359
380
|
}
|
|
360
381
|
}
|
|
361
382
|
if (!entry) {
|
|
@@ -494,6 +515,14 @@ export class StandaloneServer {
|
|
|
494
515
|
}
|
|
495
516
|
}
|
|
496
517
|
}
|
|
518
|
+
/** Send notifications/tools/list_changed to the client via stdout */
|
|
519
|
+
sendToolsChanged() {
|
|
520
|
+
if (!this.stdoutRef)
|
|
521
|
+
return;
|
|
522
|
+
const notification = { jsonrpc: "2.0", method: "notifications/tools/list_changed" };
|
|
523
|
+
this.writeResponse(this.stdoutRef, notification);
|
|
524
|
+
this.logger.info("[mcp-bridge] Sent notifications/tools/list_changed");
|
|
525
|
+
}
|
|
497
526
|
/** Extract server name from a tool name like "todoist_call" or "github_call" */
|
|
498
527
|
guessServerFromToolName(toolName) {
|
|
499
528
|
// Try exact match with placeholder pattern: serverName_call
|
|
@@ -538,6 +567,8 @@ export class StandaloneServer {
|
|
|
538
567
|
// Cache tools to disk
|
|
539
568
|
this.saveToolCache(serverName, tools);
|
|
540
569
|
this.logger.info(`[mcp-bridge] Discovered ${tools.length} tools from ${serverName}`);
|
|
570
|
+
// Notify client that tool list changed (MCP spec: notifications/tools/list_changed)
|
|
571
|
+
this.sendToolsChanged();
|
|
541
572
|
}
|
|
542
573
|
catch (err) {
|
|
543
574
|
this.logger.error(`[mcp-bridge] Failed to discover ${serverName}:`, err);
|