@winstonsayno/mcp-gateway 0.0.0-stage → 0.4.0

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.
Files changed (118) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/LICENSE +21 -0
  3. package/README.md +432 -2
  4. package/dashboard/README.md +27 -0
  5. package/dashboard/index.html +415 -0
  6. package/dist/auth/middleware.d.ts +24 -0
  7. package/dist/auth/middleware.d.ts.map +1 -0
  8. package/dist/auth/middleware.js +108 -0
  9. package/dist/auth/middleware.js.map +1 -0
  10. package/dist/auth/ratelimit.d.ts +19 -0
  11. package/dist/auth/ratelimit.d.ts.map +1 -0
  12. package/dist/auth/ratelimit.js +81 -0
  13. package/dist/auth/ratelimit.js.map +1 -0
  14. package/dist/cli.d.ts +6 -0
  15. package/dist/cli.d.ts.map +1 -0
  16. package/dist/cli.js +129 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/config/loader.d.ts +13 -0
  19. package/dist/config/loader.d.ts.map +1 -0
  20. package/dist/config/loader.js +276 -0
  21. package/dist/config/loader.js.map +1 -0
  22. package/dist/config/watcher.d.ts +29 -0
  23. package/dist/config/watcher.d.ts.map +1 -0
  24. package/dist/config/watcher.js +96 -0
  25. package/dist/config/watcher.js.map +1 -0
  26. package/dist/gateway/api.d.ts +47 -0
  27. package/dist/gateway/api.d.ts.map +1 -0
  28. package/dist/gateway/api.js +324 -0
  29. package/dist/gateway/api.js.map +1 -0
  30. package/dist/gateway/index.d.ts +49 -0
  31. package/dist/gateway/index.d.ts.map +1 -0
  32. package/dist/gateway/index.js +261 -0
  33. package/dist/gateway/index.js.map +1 -0
  34. package/dist/gateway/supervisor.d.ts +58 -0
  35. package/dist/gateway/supervisor.d.ts.map +1 -0
  36. package/dist/gateway/supervisor.js +205 -0
  37. package/dist/gateway/supervisor.js.map +1 -0
  38. package/dist/index.d.ts +18 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +14 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/middleware/cors.d.ts +19 -0
  43. package/dist/middleware/cors.d.ts.map +1 -0
  44. package/dist/middleware/cors.js +65 -0
  45. package/dist/middleware/cors.js.map +1 -0
  46. package/dist/middleware/error-handler.d.ts +40 -0
  47. package/dist/middleware/error-handler.d.ts.map +1 -0
  48. package/dist/middleware/error-handler.js +98 -0
  49. package/dist/middleware/error-handler.js.map +1 -0
  50. package/dist/middleware/request-id.d.ts +23 -0
  51. package/dist/middleware/request-id.d.ts.map +1 -0
  52. package/dist/middleware/request-id.js +30 -0
  53. package/dist/middleware/request-id.js.map +1 -0
  54. package/dist/middleware/timeout.d.ts +12 -0
  55. package/dist/middleware/timeout.d.ts.map +1 -0
  56. package/dist/middleware/timeout.js +39 -0
  57. package/dist/middleware/timeout.js.map +1 -0
  58. package/dist/monitor/index.d.ts +49 -0
  59. package/dist/monitor/index.d.ts.map +1 -0
  60. package/dist/monitor/index.js +204 -0
  61. package/dist/monitor/index.js.map +1 -0
  62. package/dist/proxy/index.d.ts +86 -0
  63. package/dist/proxy/index.d.ts.map +1 -0
  64. package/dist/proxy/index.js +389 -0
  65. package/dist/proxy/index.js.map +1 -0
  66. package/dist/registry/index.d.ts +48 -0
  67. package/dist/registry/index.d.ts.map +1 -0
  68. package/dist/registry/index.js +171 -0
  69. package/dist/registry/index.js.map +1 -0
  70. package/dist/transport/channel.d.ts +61 -0
  71. package/dist/transport/channel.d.ts.map +1 -0
  72. package/dist/transport/channel.js +38 -0
  73. package/dist/transport/channel.js.map +1 -0
  74. package/dist/transport/sse-parser.d.ts +30 -0
  75. package/dist/transport/sse-parser.d.ts.map +1 -0
  76. package/dist/transport/sse-parser.js +107 -0
  77. package/dist/transport/sse-parser.js.map +1 -0
  78. package/dist/transport/sse.d.ts +39 -0
  79. package/dist/transport/sse.d.ts.map +1 -0
  80. package/dist/transport/sse.js +142 -0
  81. package/dist/transport/sse.js.map +1 -0
  82. package/dist/transport/stdio.d.ts +35 -0
  83. package/dist/transport/stdio.d.ts.map +1 -0
  84. package/dist/transport/stdio.js +149 -0
  85. package/dist/transport/stdio.js.map +1 -0
  86. package/dist/transport/streamable-http.d.ts +45 -0
  87. package/dist/transport/streamable-http.d.ts.map +1 -0
  88. package/dist/transport/streamable-http.js +183 -0
  89. package/dist/transport/streamable-http.js.map +1 -0
  90. package/dist/transport/websocket.d.ts +38 -0
  91. package/dist/transport/websocket.d.ts.map +1 -0
  92. package/dist/transport/websocket.js +172 -0
  93. package/dist/transport/websocket.js.map +1 -0
  94. package/dist/utils/logger.d.ts +16 -0
  95. package/dist/utils/logger.d.ts.map +1 -0
  96. package/dist/utils/logger.js +60 -0
  97. package/dist/utils/logger.js.map +1 -0
  98. package/dist/utils/mutex.d.ts +25 -0
  99. package/dist/utils/mutex.d.ts.map +1 -0
  100. package/dist/utils/mutex.js +62 -0
  101. package/dist/utils/mutex.js.map +1 -0
  102. package/dist/utils/semaphore.d.ts +16 -0
  103. package/dist/utils/semaphore.d.ts.map +1 -0
  104. package/dist/utils/semaphore.js +42 -0
  105. package/dist/utils/semaphore.js.map +1 -0
  106. package/dist/utils/tool-filter.d.ts +24 -0
  107. package/dist/utils/tool-filter.d.ts.map +1 -0
  108. package/dist/utils/tool-filter.js +48 -0
  109. package/dist/utils/tool-filter.js.map +1 -0
  110. package/dist/utils/types.d.ts +223 -0
  111. package/dist/utils/types.d.ts.map +1 -0
  112. package/dist/utils/types.js +5 -0
  113. package/dist/utils/types.js.map +1 -0
  114. package/dist/utils/version.d.ts +6 -0
  115. package/dist/utils/version.d.ts.map +1 -0
  116. package/dist/utils/version.js +18 -0
  117. package/dist/utils/version.js.map +1 -0
  118. package/package.json +79 -4
@@ -0,0 +1,86 @@
1
+ /**
2
+ * MCP Proxy
3
+ * Routes tool-call requests to the appropriate upstream MCP server over any
4
+ * supported transport: `stdio`, `sse` (HTTP+SSE, 2024-11-05), `websocket`
5
+ * and `streamable-http` (2025-03-26+).
6
+ *
7
+ * The proxy is split in two layers:
8
+ * - a *channel* per transport (`src/transport/*`) that only moves JSON-RPC
9
+ * messages, and
10
+ * - this transport-independent *session* layer: MCP handshake, request/
11
+ * response correlation, timeouts + upstream cancellation, `maxConcurrency`,
12
+ * answering server→client requests (`ping`), `tools/list` pagination and
13
+ * `notifications/tools/list_changed`.
14
+ *
15
+ * Events:
16
+ * - `disconnected` (serverId, error) — a session was lost unexpectedly
17
+ * (process exit, socket/stream closed, session expired). Not emitted for
18
+ * `disconnect()`. The gateway supervisor uses it to reconnect.
19
+ * - `tools-changed` (serverId, tools) — the server announced a new tool list.
20
+ *
21
+ * Fixes kept from v0.2.0 / the audit: per-server connect mutex (BUG-001),
22
+ * monotonic ids (BUG-002), no leaked sessions on failed `initialize`, an old
23
+ * session's exit can never remove a newer one, server requests with colliding
24
+ * ids are answered instead of mistaken for responses.
25
+ *
26
+ * @module proxy
27
+ */
28
+ import { EventEmitter } from 'events';
29
+ import type { McpServerConfig, ProxyResponse, ToolInfo } from '../utils/types.js';
30
+ import type { ChannelFactory } from '../transport/channel.js';
31
+ /** Protocol version the gateway asks for in `initialize`. */
32
+ export declare const MCP_PROTOCOL_VERSION = "2025-06-18";
33
+ /** Versions the gateway understands; servers may answer with any of them. */
34
+ export declare const SUPPORTED_PROTOCOL_VERSIONS: string[];
35
+ export declare const ERR_NOT_CONNECTED = -32000;
36
+ export declare const ERR_TIMEOUT = -32001;
37
+ /** Default channel per transport. */
38
+ export declare const defaultChannelFactory: ChannelFactory;
39
+ export interface SessionInfo {
40
+ transport: string;
41
+ protocolVersion?: string;
42
+ serverInfo?: {
43
+ name?: string;
44
+ version?: string;
45
+ };
46
+ connectedAt?: Date;
47
+ }
48
+ export interface ProxyOptions {
49
+ /** Grace period between closing stdin / SIGTERM / SIGKILL (and for WS/HTTP close). */
50
+ killGraceMs?: number;
51
+ /** Override how channels are created (for tests or custom transports). */
52
+ channelFactory?: ChannelFactory;
53
+ }
54
+ export declare class McpProxy extends EventEmitter {
55
+ private sessions;
56
+ private spawnLocks;
57
+ private readonly killGraceMs;
58
+ private readonly channelFactory;
59
+ constructor(options?: ProxyOptions);
60
+ private getSpawnLock;
61
+ connect(config: McpServerConfig): Promise<ToolInfo[]>;
62
+ private _handshake;
63
+ private _listTools;
64
+ disconnect(serverId: string): Promise<void>;
65
+ private _disconnectUnlocked;
66
+ disconnectAll(): Promise<void>;
67
+ callTool(serverId: string, toolName: string, args: Record<string, unknown>, timeout?: number): Promise<ProxyResponse>;
68
+ /** MCP `ping`: resolves with the round-trip latency (ms), or rejects if the server does not answer. */
69
+ ping(serverId: string, timeout?: number): Promise<number>;
70
+ isConnected(serverId: string): boolean;
71
+ /** Negotiated session details (for /servers). */
72
+ getSessionInfo(serverId: string): SessionInfo | undefined;
73
+ /** In-flight and queued tool calls for a server (for monitoring). */
74
+ getLoad(serverId: string): {
75
+ inFlight: number;
76
+ queued: number;
77
+ } | undefined;
78
+ private _onChannelLost;
79
+ private _rejectAll;
80
+ private _onMessage;
81
+ private _refreshTools;
82
+ private _answerServerRequest;
83
+ private _notify;
84
+ private _sendRequest;
85
+ }
86
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/proxy/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAKlF,OAAO,KAAK,EAAE,cAAc,EAA8D,MAAM,yBAAyB,CAAC;AAM1H,6DAA6D;AAC7D,eAAO,MAAM,oBAAoB,eAAe,CAAC;AACjD,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,UAA6C,CAAC;AAMtF,eAAO,MAAM,iBAAiB,SAAS,CAAC;AACxC,eAAO,MAAM,WAAW,SAAS,CAAC;AAQlC,qCAAqC;AACrC,eAAO,MAAM,qBAAqB,EAAE,cAanC,CAAC;AAqBF,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,WAAW,CAAC,EAAE,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,qBAAa,QAAS,SAAQ,YAAY;IACxC,OAAO,CAAC,QAAQ,CAA8B;IAE9C,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;gBAEpC,OAAO,GAAE,YAAiB;IAMtC,OAAO,CAAC,YAAY;IAWd,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;YA0C7C,UAAU;YAoCV,UAAU;IAgClB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAInC,mBAAmB;IAY3B,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAM9B,QAAQ,CACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC;IAkBzB,uGAAuG;IACjG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,SAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAY9D,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAKtC,iDAAiD;IACjD,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAWzD,qEAAqE;IACrE,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS;IAO3E,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,UAAU;IAyBlB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,oBAAoB;YAQd,OAAO;YAOP,YAAY;CA+E3B"}
@@ -0,0 +1,389 @@
1
+ /**
2
+ * MCP Proxy
3
+ * Routes tool-call requests to the appropriate upstream MCP server over any
4
+ * supported transport: `stdio`, `sse` (HTTP+SSE, 2024-11-05), `websocket`
5
+ * and `streamable-http` (2025-03-26+).
6
+ *
7
+ * The proxy is split in two layers:
8
+ * - a *channel* per transport (`src/transport/*`) that only moves JSON-RPC
9
+ * messages, and
10
+ * - this transport-independent *session* layer: MCP handshake, request/
11
+ * response correlation, timeouts + upstream cancellation, `maxConcurrency`,
12
+ * answering server→client requests (`ping`), `tools/list` pagination and
13
+ * `notifications/tools/list_changed`.
14
+ *
15
+ * Events:
16
+ * - `disconnected` (serverId, error) — a session was lost unexpectedly
17
+ * (process exit, socket/stream closed, session expired). Not emitted for
18
+ * `disconnect()`. The gateway supervisor uses it to reconnect.
19
+ * - `tools-changed` (serverId, tools) — the server announced a new tool list.
20
+ *
21
+ * Fixes kept from v0.2.0 / the audit: per-server connect mutex (BUG-001),
22
+ * monotonic ids (BUG-002), no leaked sessions on failed `initialize`, an old
23
+ * session's exit can never remove a newer one, server requests with colliding
24
+ * ids are answered instead of mistaken for responses.
25
+ *
26
+ * @module proxy
27
+ */
28
+ import { EventEmitter } from 'events';
29
+ import { logger } from '../utils/logger.js';
30
+ import { Mutex } from '../utils/mutex.js';
31
+ import { Semaphore } from '../utils/semaphore.js';
32
+ import { VERSION } from '../utils/version.js';
33
+ import { StdioChannel } from '../transport/stdio.js';
34
+ import { SseChannel } from '../transport/sse.js';
35
+ import { WebSocketChannel } from '../transport/websocket.js';
36
+ import { StreamableHttpChannel } from '../transport/streamable-http.js';
37
+ /** Protocol version the gateway asks for in `initialize`. */
38
+ export const MCP_PROTOCOL_VERSION = '2025-06-18';
39
+ /** Versions the gateway understands; servers may answer with any of them. */
40
+ export const SUPPORTED_PROTOCOL_VERSIONS = ['2025-06-18', '2025-03-26', '2024-11-05'];
41
+ const MAX_TOOL_PAGES = 100;
42
+ const DEFAULT_TIMEOUT_MS = 30_000;
43
+ // JSON-RPC error codes used by the gateway
44
+ export const ERR_NOT_CONNECTED = -32000;
45
+ export const ERR_TIMEOUT = -32001;
46
+ // ── Monotonic ID counter (fix BUG-002) ──────────────────────────────────────
47
+ let _idSeq = 0;
48
+ function nextId() {
49
+ return ++_idSeq;
50
+ }
51
+ /** Default channel per transport. */
52
+ export const defaultChannelFactory = (config, options) => {
53
+ switch (config.transport) {
54
+ case 'stdio':
55
+ return new StdioChannel(config, options);
56
+ case 'sse':
57
+ return new SseChannel(config, options);
58
+ case 'websocket':
59
+ return new WebSocketChannel(config, options);
60
+ case 'streamable-http':
61
+ return new StreamableHttpChannel(config, options);
62
+ default:
63
+ throw new Error(`Unknown transport "${String(config.transport)}" (server "${config.id}")`);
64
+ }
65
+ };
66
+ export class McpProxy extends EventEmitter {
67
+ sessions = new Map();
68
+ // Per-server connect mutex (fix BUG-001)
69
+ spawnLocks = new Map();
70
+ killGraceMs;
71
+ channelFactory;
72
+ constructor(options = {}) {
73
+ super();
74
+ this.killGraceMs = options.killGraceMs ?? 2_000;
75
+ this.channelFactory = options.channelFactory ?? defaultChannelFactory;
76
+ }
77
+ getSpawnLock(serverId) {
78
+ let lock = this.spawnLocks.get(serverId);
79
+ if (!lock) {
80
+ lock = new Mutex();
81
+ this.spawnLocks.set(serverId, lock);
82
+ }
83
+ return lock;
84
+ }
85
+ // ─── Connection Management ──────────────────────────────────────────────────
86
+ async connect(config) {
87
+ // Serialise concurrent connect calls for the same server (fix BUG-001)
88
+ return this.getSpawnLock(config.id).runExclusive(async () => {
89
+ // Replace (and clean up) any existing session for this id.
90
+ if (this.sessions.has(config.id)) {
91
+ await this._disconnectUnlocked(config.id);
92
+ }
93
+ const timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
94
+ const options = { connectTimeoutMs: timeout, killGraceMs: this.killGraceMs };
95
+ const channel = this.channelFactory(config, options);
96
+ const session = {
97
+ config,
98
+ channel,
99
+ pendingRequests: new Map(),
100
+ limiter: new Semaphore(config.maxConcurrency ?? Infinity),
101
+ closed: false,
102
+ };
103
+ channel.onmessage = (msg) => this._onMessage(session, msg);
104
+ channel.onclose = (err) => this._onChannelLost(session, err);
105
+ try {
106
+ await withTimeout(channel.start(), timeout, `Connecting to "${config.id}" timed out after ${timeout}ms`);
107
+ }
108
+ catch (err) {
109
+ session.closed = true;
110
+ await channel.close().catch(() => { });
111
+ throw err;
112
+ }
113
+ this.sessions.set(config.id, session);
114
+ try {
115
+ const tools = await this._handshake(session);
116
+ session.connectedAt = new Date();
117
+ return tools;
118
+ }
119
+ catch (err) {
120
+ await this._disconnectUnlocked(config.id);
121
+ throw err;
122
+ }
123
+ });
124
+ }
125
+ async _handshake(session) {
126
+ const { config } = session;
127
+ const timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
128
+ const initResult = await this._sendRequest(session, 'initialize', {
129
+ protocolVersion: MCP_PROTOCOL_VERSION,
130
+ capabilities: {},
131
+ clientInfo: { name: 'mcp-gateway', version: VERSION },
132
+ }, timeout, false);
133
+ if (!initResult.success) {
134
+ throw new Error(`Failed to initialize server "${config.id}": ${initResult.error?.message}`);
135
+ }
136
+ const init = (initResult.result ?? {});
137
+ if (typeof init.protocolVersion === 'string') {
138
+ session.protocolVersion = init.protocolVersion;
139
+ if (!SUPPORTED_PROTOCOL_VERSIONS.includes(init.protocolVersion)) {
140
+ logger.warn(`Server "${config.id}" negotiated unknown protocol version ${init.protocolVersion}; continuing`);
141
+ }
142
+ session.channel.setProtocolVersion?.(init.protocolVersion);
143
+ }
144
+ session.serverInfo = init.serverInfo;
145
+ await this._notify(session, 'notifications/initialized');
146
+ return this._listTools(session);
147
+ }
148
+ async _listTools(session) {
149
+ const { config } = session;
150
+ const timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
151
+ const tools = [];
152
+ let cursor;
153
+ for (let page = 0; page < MAX_TOOL_PAGES; page++) {
154
+ const toolsResult = await this._sendRequest(session, 'tools/list', cursor ? { cursor } : {}, timeout, false);
155
+ if (!toolsResult.success) {
156
+ logger.warn(`Could not list tools for "${config.id}": ${toolsResult.error?.message}`);
157
+ break;
158
+ }
159
+ const result = (toolsResult.result ?? {});
160
+ for (const t of result.tools ?? []) {
161
+ const tool = t;
162
+ if (typeof tool?.name !== 'string')
163
+ continue;
164
+ tools.push({
165
+ name: tool.name,
166
+ description: tool.description,
167
+ inputSchema: tool.inputSchema,
168
+ serverId: config.id,
169
+ serverName: config.name,
170
+ });
171
+ }
172
+ cursor = typeof result.nextCursor === 'string' && result.nextCursor ? result.nextCursor : undefined;
173
+ if (!cursor)
174
+ break;
175
+ }
176
+ return tools;
177
+ }
178
+ async disconnect(serverId) {
179
+ await this.getSpawnLock(serverId).runExclusive(() => this._disconnectUnlocked(serverId));
180
+ }
181
+ async _disconnectUnlocked(serverId) {
182
+ const session = this.sessions.get(serverId);
183
+ if (!session)
184
+ return;
185
+ this.sessions.delete(serverId);
186
+ session.closed = true;
187
+ this._rejectAll(session, new Error('Server disconnected'));
188
+ await session.channel.close().catch((err) => {
189
+ logger.debug(`[${serverId}] error while closing: ${String(err)}`);
190
+ });
191
+ logger.info(`Disconnected from server: ${serverId}`);
192
+ }
193
+ async disconnectAll() {
194
+ await Promise.all([...this.sessions.keys()].map((id) => this.disconnect(id)));
195
+ }
196
+ // ─── Tool Execution ─────────────────────────────────────────────────────────
197
+ async callTool(serverId, toolName, args, timeout) {
198
+ const session = this.sessions.get(serverId);
199
+ if (!session || session.closed) {
200
+ return {
201
+ success: false,
202
+ error: { code: ERR_NOT_CONNECTED, message: `Server "${serverId}" is not connected` },
203
+ durationMs: 0,
204
+ };
205
+ }
206
+ return this._sendRequest(session, 'tools/call', { name: toolName, arguments: args }, timeout ?? session.config.timeout ?? DEFAULT_TIMEOUT_MS, true);
207
+ }
208
+ /** MCP `ping`: resolves with the round-trip latency (ms), or rejects if the server does not answer. */
209
+ async ping(serverId, timeout = 5_000) {
210
+ const session = this.sessions.get(serverId);
211
+ if (!session || session.closed)
212
+ throw new Error(`Server "${serverId}" is not connected`);
213
+ const r = await this._sendRequest(session, 'ping', undefined, timeout, false);
214
+ // Any JSON-RPC answer (even "method not found" from a non-compliant
215
+ // server) proves the server is alive; only timeouts / transport errors fail.
216
+ if (!r.success && (r.error?.code === ERR_TIMEOUT || r.error?.code === ERR_NOT_CONNECTED)) {
217
+ throw new Error(r.error.message);
218
+ }
219
+ return r.durationMs;
220
+ }
221
+ isConnected(serverId) {
222
+ const s = this.sessions.get(serverId);
223
+ return !!s && !s.closed;
224
+ }
225
+ /** Negotiated session details (for /servers). */
226
+ getSessionInfo(serverId) {
227
+ const s = this.sessions.get(serverId);
228
+ if (!s || s.closed)
229
+ return undefined;
230
+ return {
231
+ transport: s.channel.kind,
232
+ protocolVersion: s.protocolVersion,
233
+ serverInfo: s.serverInfo,
234
+ connectedAt: s.connectedAt,
235
+ };
236
+ }
237
+ /** In-flight and queued tool calls for a server (for monitoring). */
238
+ getLoad(serverId) {
239
+ const s = this.sessions.get(serverId);
240
+ return s ? { inFlight: s.limiter.inFlight, queued: s.limiter.pending } : undefined;
241
+ }
242
+ // ─── Internal ───────────────────────────────────────────────────────────────
243
+ _onChannelLost(session, err) {
244
+ if (session.closed)
245
+ return;
246
+ session.closed = true;
247
+ this._rejectAll(session, err);
248
+ // Only drop the session if it is still the current one for this id.
249
+ if (this.sessions.get(session.config.id) === session) {
250
+ this.sessions.delete(session.config.id);
251
+ // A loss during the handshake surfaces as a connect() failure instead.
252
+ if (session.connectedAt)
253
+ this.emit('disconnected', session.config.id, err);
254
+ }
255
+ }
256
+ _rejectAll(session, err) {
257
+ for (const [, pending] of session.pendingRequests) {
258
+ clearTimeout(pending.timer);
259
+ pending.reject(err);
260
+ }
261
+ session.pendingRequests.clear();
262
+ }
263
+ _onMessage(session, msg) {
264
+ if (!msg || typeof msg !== 'object')
265
+ return;
266
+ if (typeof msg.method === 'string') {
267
+ if (msg.id !== undefined && msg.id !== null) {
268
+ this._answerServerRequest(session, msg.id, msg.method);
269
+ }
270
+ else if (msg.method === 'notifications/tools/list_changed') {
271
+ this._refreshTools(session);
272
+ }
273
+ return;
274
+ }
275
+ if (msg.id === undefined || msg.id === null)
276
+ return;
277
+ const pending = session.pendingRequests.get(msg.id);
278
+ if (!pending)
279
+ return;
280
+ clearTimeout(pending.timer);
281
+ session.pendingRequests.delete(msg.id);
282
+ if (msg.error) {
283
+ pending.resolve({ success: false, error: msg.error, durationMs: 0 });
284
+ }
285
+ else {
286
+ pending.resolve({ success: true, result: msg.result, durationMs: 0 });
287
+ }
288
+ }
289
+ _refreshTools(session) {
290
+ if (!session.connectedAt)
291
+ return; // still handshaking: the initial list is fetched anyway
292
+ void this._listTools(session).then((tools) => {
293
+ if (!session.closed)
294
+ this.emit('tools-changed', session.config.id, tools);
295
+ }, (err) => logger.warn(`[${session.config.id}] could not refresh tools: ${String(err)}`));
296
+ }
297
+ _answerServerRequest(session, id, method) {
298
+ const reply = method === 'ping'
299
+ ? { jsonrpc: '2.0', id, result: {} }
300
+ : { jsonrpc: '2.0', id, error: { code: -32601, message: `Method not supported by gateway: ${method}` } };
301
+ void session.channel.send(reply).catch(() => { });
302
+ }
303
+ async _notify(session, method, params) {
304
+ const msg = params === undefined ? { jsonrpc: '2.0', method } : { jsonrpc: '2.0', method, params };
305
+ await session.channel.send(msg).catch((err) => {
306
+ logger.debug(`[${session.config.id}] failed to send ${method}: ${String(err)}`);
307
+ });
308
+ }
309
+ async _sendRequest(session, method, params, timeout = DEFAULT_TIMEOUT_MS, limited = true) {
310
+ const serverId = session.config.id;
311
+ const startTime = Date.now();
312
+ const deadline = startTime + timeout;
313
+ const timedOut = () => ({
314
+ success: false,
315
+ error: { code: ERR_TIMEOUT, message: `Request timed out after ${timeout}ms` },
316
+ durationMs: Date.now() - startTime,
317
+ });
318
+ const notConnected = (message) => ({
319
+ success: false,
320
+ error: { code: ERR_NOT_CONNECTED, message },
321
+ durationMs: Date.now() - startTime,
322
+ });
323
+ if (session.closed)
324
+ return notConnected(`No session for server "${serverId}"`);
325
+ // Enforce maxConcurrency; the timeout covers time spent queued.
326
+ let release;
327
+ if (limited) {
328
+ let queueTimer;
329
+ const slot = session.limiter.acquire();
330
+ const winner = await Promise.race([
331
+ slot.then((r) => ({ release: r })),
332
+ new Promise((r) => (queueTimer = setTimeout(() => r(null), timeout))),
333
+ ]);
334
+ clearTimeout(queueTimer);
335
+ if (!winner) {
336
+ // Give the slot back as soon as it is granted.
337
+ void slot.then((r) => r());
338
+ return timedOut();
339
+ }
340
+ release = winner.release;
341
+ }
342
+ try {
343
+ if (session.closed)
344
+ return notConnected(`Server "${serverId}" disconnected`);
345
+ return await new Promise((resolve) => {
346
+ const id = nextId();
347
+ const remaining = Math.max(0, deadline - Date.now());
348
+ const timer = setTimeout(() => {
349
+ session.pendingRequests.delete(id);
350
+ // Ask the server to stop working on it, then free channel resources.
351
+ void session.channel
352
+ .send({ jsonrpc: '2.0', method: 'notifications/cancelled', params: { requestId: id, reason: 'timeout' } })
353
+ .catch(() => { })
354
+ .finally(() => session.channel.abandon?.(id));
355
+ resolve(timedOut());
356
+ }, remaining);
357
+ session.pendingRequests.set(id, {
358
+ resolve: (response) => resolve({ ...response, durationMs: Date.now() - startTime }),
359
+ reject: (err) => resolve(notConnected(err.message)),
360
+ timer,
361
+ });
362
+ const payload = params === undefined ? { jsonrpc: '2.0', id, method } : { jsonrpc: '2.0', id, method, params };
363
+ session.channel.send(payload).catch((err) => {
364
+ const pending = session.pendingRequests.get(id);
365
+ if (!pending)
366
+ return; // already answered / timed out
367
+ clearTimeout(timer);
368
+ session.pendingRequests.delete(id);
369
+ const message = err instanceof Error ? err.message : String(err);
370
+ resolve(notConnected(`Request to "${serverId}" failed: ${message}`));
371
+ });
372
+ });
373
+ }
374
+ finally {
375
+ release?.();
376
+ }
377
+ }
378
+ }
379
+ function withTimeout(p, ms, message) {
380
+ let timer;
381
+ return Promise.race([
382
+ p,
383
+ new Promise((_, reject) => {
384
+ timer = setTimeout(() => reject(new Error(message)), ms);
385
+ timer.unref();
386
+ }),
387
+ ]).finally(() => clearTimeout(timer));
388
+ }
389
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/proxy/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * MCP Server Registry
3
+ * Manages the lifecycle of all registered MCP servers
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ import type { McpServerConfig, ServerHealth, ServerStatus, ToolInfo } from '../utils/types.js';
7
+ export declare class ServerRegistry extends EventEmitter {
8
+ private readonly healthCheckMs;
9
+ private servers;
10
+ private health;
11
+ private tools;
12
+ private healthCheckInterval?;
13
+ constructor(healthCheckMs?: number);
14
+ register(config: McpServerConfig): void;
15
+ unregister(serverId: string): boolean;
16
+ getServer(id: string): McpServerConfig | undefined;
17
+ getAllServers(): McpServerConfig[];
18
+ getEnabledServers(): McpServerConfig[];
19
+ getServersByTag(tag: string): McpServerConfig[];
20
+ getHealth(serverId: string): ServerHealth | undefined;
21
+ getAllHealth(): ServerHealth[];
22
+ /**
23
+ * Store a server's tool list, applying its `tools` allow/deny filter.
24
+ * Only exposed tools are kept, so discovery, routing and counts never see
25
+ * filtered ones.
26
+ */
27
+ setTools(serverId: string, all: ToolInfo[]): void;
28
+ /** Whether the server's `tools` filter lets `toolName` through. */
29
+ isToolExposed(serverId: string, toolName: string): boolean;
30
+ getTools(serverId: string): ToolInfo[];
31
+ getAllTools(): ToolInfo[];
32
+ /** Every server's entry for a tool name (more than one means the name is ambiguous). */
33
+ findTools(toolName: string): ToolInfo[];
34
+ findTool(toolName: string): ToolInfo | undefined;
35
+ updateHealth(serverId: string, status: ServerStatus, latencyMs?: number, errorMessage?: string, extra?: Pick<ServerHealth, 'connectedSince' | 'reconnect'>): void;
36
+ startHealthChecks(checkFn: (serverId: string) => Promise<void>): void;
37
+ stopHealthChecks(): void;
38
+ getSummary(): {
39
+ total: number;
40
+ online: number;
41
+ offline: number;
42
+ degraded: number;
43
+ reconnecting: number;
44
+ unknown: number;
45
+ totalTools: number;
46
+ };
47
+ }
48
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/registry/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAI3B,qBAAa,cAAe,SAAQ,YAAY;IAMlC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAL1C,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,mBAAmB,CAAC,CAAiB;gBAEhB,aAAa,SAAS;IAMnD,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAcvC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAYrC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAIlD,aAAa,IAAI,eAAe,EAAE;IAIlC,iBAAiB,IAAI,eAAe,EAAE;IAItC,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE;IAI/C,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIrD,YAAY,IAAI,YAAY,EAAE;IAM9B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,IAAI;IAkBjD,mEAAmE;IACnE,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE;IAItC,WAAW,IAAI,QAAQ,EAAE;IAIzB,wFAAwF;IACxF,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE;IASvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAUhD,YAAY,CACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,EACpB,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,KAAK,GAAE,IAAI,CAAC,YAAY,EAAE,gBAAgB,GAAG,WAAW,CAAM,GAC7D,IAAI;IAyBP,iBAAiB,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAmBrE,gBAAgB,IAAI,IAAI;IASxB,UAAU,IAAI;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB;CAYF"}