@aiwerk/mcp-bridge 1.1.7 → 1.1.8

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.
@@ -7,7 +7,7 @@ export type { RouterToolHint, RouterServerStatus, RouterDispatchResponse, Router
7
7
  export { convertJsonSchemaToTypeBox, createToolParameters, setTypeBoxLoader, setSchemaLogger } from "./schema-convert.js";
8
8
  export { initializeProtocol, fetchToolsList, PACKAGE_VERSION } from "./protocol.js";
9
9
  export { loadConfig, parseEnvFile, initConfigDir, getConfigDir } from "./config.js";
10
- export type { Logger, McpServerConfig, McpClientConfig, McpTool, McpRequest, McpCallRequest, McpResponse, McpTransport, McpServerConnection, BridgeConfig, } from "./types.js";
10
+ export type { Logger, McpServerConfig, McpClientConfig, McpTool, McpRequest, McpCallRequest, McpResponse, JsonRpcMessage, McpTransport, McpServerConnection, BridgeConfig, } from "./types.js";
11
11
  export { nextRequestId } from "./types.js";
12
12
  export { pickRegisteredToolName } from "./tool-naming.js";
13
13
  export { StandaloneServer } from "./standalone-server.js";
@@ -89,9 +89,17 @@ export class StandaloneServer {
89
89
  switch (request.method) {
90
90
  case "initialize":
91
91
  return this.handleInitialize(id);
92
+ case "notifications/initialized":
93
+ return { jsonrpc: "2.0", id, result: {} };
92
94
  case "tools/list":
95
+ if (!this.initialized) {
96
+ return { jsonrpc: "2.0", id, error: { code: -32002, message: "Server not initialized. Call 'initialize' first." } };
97
+ }
93
98
  return this.handleToolsList(id);
94
99
  case "tools/call":
100
+ if (!this.initialized) {
101
+ return { jsonrpc: "2.0", id, error: { code: -32002, message: "Server not initialized. Call 'initialize' first." } };
102
+ }
95
103
  return this.handleToolsCall(id, request.params);
96
104
  case "ping":
97
105
  return { jsonrpc: "2.0", id, result: {} };
@@ -254,12 +262,16 @@ export class StandaloneServer {
254
262
  await this.discoveryPromise;
255
263
  return;
256
264
  }
257
- this.discoveryPromise = this._doDiscovery(force);
265
+ const promise = this._doDiscovery(force);
266
+ this.discoveryPromise = promise;
258
267
  try {
259
- await this.discoveryPromise;
268
+ await promise;
260
269
  }
261
270
  finally {
262
- this.discoveryPromise = undefined;
271
+ // Only clear if we're still the active promise (a force call may have replaced us)
272
+ if (this.discoveryPromise === promise) {
273
+ this.discoveryPromise = undefined;
274
+ }
263
275
  }
264
276
  }
265
277
  async _doDiscovery(force) {
@@ -1,4 +1,4 @@
1
- import { McpTransport, McpRequest, McpResponse, McpServerConfig, McpClientConfig, Logger } from "./types.js";
1
+ import { McpTransport, McpRequest, McpResponse, McpServerConfig, McpClientConfig, Logger, JsonRpcMessage } from "./types.js";
2
2
  export type PendingRequest = {
3
3
  resolve: (value: McpResponse) => void;
4
4
  reject: (reason: Error) => void;
@@ -35,7 +35,7 @@ export declare abstract class BaseTransport implements McpTransport {
35
35
  * - Other notifications -> debug log
36
36
  * - Responses with id -> resolve/reject matching pending request
37
37
  */
38
- protected handleMessage(message: any): void;
38
+ protected handleMessage(message: JsonRpcMessage): void;
39
39
  /** Reject and clear all pending requests with the given reason. */
40
40
  protected rejectAllPending(reason: string): void;
41
41
  /**
@@ -44,15 +44,18 @@ export class BaseTransport {
44
44
  this.logger.debug(`[mcp-bridge] Unhandled ${this.transportName} notification: ${message.method}`);
45
45
  return;
46
46
  }
47
- if (hasId && this.pendingRequests.has(message.id)) {
48
- const pending = this.pendingRequests.get(message.id);
49
- clearTimeout(pending.timeout);
50
- this.pendingRequests.delete(message.id);
51
- if (message.error) {
52
- pending.reject(new Error(message.error.message || "MCP error"));
53
- }
54
- else {
55
- pending.resolve(message);
47
+ if (hasId) {
48
+ const id = message.id;
49
+ if (this.pendingRequests.has(id)) {
50
+ const pending = this.pendingRequests.get(id);
51
+ clearTimeout(pending.timeout);
52
+ this.pendingRequests.delete(id);
53
+ if (message.error) {
54
+ pending.reject(new Error(message.error.message || "MCP error"));
55
+ }
56
+ else {
57
+ pending.resolve({ jsonrpc: "2.0", id, result: message.result });
58
+ }
56
59
  }
57
60
  }
58
61
  }
@@ -30,6 +30,18 @@ export interface McpTool {
30
30
  description: string;
31
31
  inputSchema: any;
32
32
  }
33
+ /** Incoming JSON-RPC message (response or notification). */
34
+ export interface JsonRpcMessage {
35
+ jsonrpc: "2.0";
36
+ id?: number | null;
37
+ method?: string;
38
+ result?: any;
39
+ error?: {
40
+ code: number;
41
+ message: string;
42
+ data?: unknown;
43
+ };
44
+ }
33
45
  /** MCP JSON-RPC request. id is required for requests (omit only for notifications). */
34
46
  export interface McpRequest {
35
47
  jsonrpc: "2.0";
@@ -1,4 +1,4 @@
1
- import { execSync, exec as execCb, execFile } from "child_process";
1
+ import { execFileSync, execFile } from "child_process";
2
2
  import { PACKAGE_VERSION } from "./protocol.js";
3
3
  const PACKAGE_NAME = "@aiwerk/mcp-bridge";
4
4
  let cachedUpdateInfo = null;
@@ -66,7 +66,8 @@ export async function runUpdate(logger) {
66
66
  }
67
67
  logger.info(`[mcp-bridge] Running update: ${info.updateCommand}`);
68
68
  try {
69
- const output = await execAsync(info.updateCommand, 60_000);
69
+ const parts = info.updateCommand.split(/\s+/);
70
+ const output = await execFileAsync(parts[0], parts.slice(1), 60_000);
70
71
  // Invalidate cache so next check re-fetches
71
72
  cachedUpdateInfo = null;
72
73
  noticeDelivered = false;
@@ -100,16 +101,16 @@ function npmViewVersion(_logger) {
100
101
  }
101
102
  function npmViewVersionSync(_logger) {
102
103
  try {
103
- return execSync(`npm view ${PACKAGE_NAME} version`, { encoding: "utf-8", timeout: 10_000 }).trim();
104
+ return execFileSync("npm", ["view", PACKAGE_NAME, "version"], { encoding: "utf-8", timeout: 10_000 }).trim();
104
105
  }
105
106
  catch {
106
107
  return "unknown";
107
108
  }
108
109
  }
109
- function execAsync(cmd, timeoutMs) {
110
+ function execFileAsync(file, args, timeoutMs) {
110
111
  return new Promise((resolve, reject) => {
111
112
  const timeout = setTimeout(() => reject(new Error(`Command timed out after ${timeoutMs}ms`)), timeoutMs);
112
- execCb(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout, stderr) => {
113
+ execFile(file, args, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout, stderr) => {
113
114
  clearTimeout(timeout);
114
115
  if (err)
115
116
  return reject(new Error(`${err.message}\n${stderr ?? ""}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwerk/mcp-bridge",
3
- "version": "1.1.7",
3
+ "version": "1.1.8",
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",