@mcp-web/bridge 0.1.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 (72) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +311 -0
  3. package/dist/adapters/bun.d.ts +95 -0
  4. package/dist/adapters/bun.d.ts.map +1 -0
  5. package/dist/adapters/bun.js +286 -0
  6. package/dist/adapters/bun.js.map +1 -0
  7. package/dist/adapters/deno.d.ts +89 -0
  8. package/dist/adapters/deno.d.ts.map +1 -0
  9. package/dist/adapters/deno.js +249 -0
  10. package/dist/adapters/deno.js.map +1 -0
  11. package/dist/adapters/index.d.ts +21 -0
  12. package/dist/adapters/index.d.ts.map +1 -0
  13. package/dist/adapters/index.js +21 -0
  14. package/dist/adapters/index.js.map +1 -0
  15. package/dist/adapters/node.d.ts +112 -0
  16. package/dist/adapters/node.d.ts.map +1 -0
  17. package/dist/adapters/node.js +309 -0
  18. package/dist/adapters/node.js.map +1 -0
  19. package/dist/adapters/partykit.d.ts +153 -0
  20. package/dist/adapters/partykit.d.ts.map +1 -0
  21. package/dist/adapters/partykit.js +372 -0
  22. package/dist/adapters/partykit.js.map +1 -0
  23. package/dist/bridge.d.ts +38 -0
  24. package/dist/bridge.d.ts.map +1 -0
  25. package/dist/bridge.js +1004 -0
  26. package/dist/bridge.js.map +1 -0
  27. package/dist/core.d.ts +75 -0
  28. package/dist/core.d.ts.map +1 -0
  29. package/dist/core.js +1508 -0
  30. package/dist/core.js.map +1 -0
  31. package/dist/index.d.ts +38 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +42 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/runtime/index.d.ts +11 -0
  36. package/dist/runtime/index.d.ts.map +1 -0
  37. package/dist/runtime/index.js +9 -0
  38. package/dist/runtime/index.js.map +1 -0
  39. package/dist/runtime/scheduler.d.ts +69 -0
  40. package/dist/runtime/scheduler.d.ts.map +1 -0
  41. package/dist/runtime/scheduler.js +88 -0
  42. package/dist/runtime/scheduler.js.map +1 -0
  43. package/dist/runtime/types.d.ts +144 -0
  44. package/dist/runtime/types.d.ts.map +1 -0
  45. package/dist/runtime/types.js +82 -0
  46. package/dist/runtime/types.js.map +1 -0
  47. package/dist/schemas.d.ts +6 -0
  48. package/dist/schemas.d.ts.map +1 -0
  49. package/dist/schemas.js +6 -0
  50. package/dist/schemas.js.map +1 -0
  51. package/dist/types.d.ts +130 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +2 -0
  54. package/dist/types.js.map +1 -0
  55. package/package.json +28 -0
  56. package/src/adapters/bun.ts +354 -0
  57. package/src/adapters/deno.ts +282 -0
  58. package/src/adapters/index.ts +28 -0
  59. package/src/adapters/node.ts +385 -0
  60. package/src/adapters/partykit.ts +482 -0
  61. package/src/bridge.test.ts +64 -0
  62. package/src/core.ts +2176 -0
  63. package/src/index.ts +90 -0
  64. package/src/limits.test.ts +436 -0
  65. package/src/remote-mcp.test.ts +770 -0
  66. package/src/runtime/index.ts +24 -0
  67. package/src/runtime/scheduler.ts +130 -0
  68. package/src/runtime/types.ts +229 -0
  69. package/src/schemas.ts +6 -0
  70. package/src/session-naming.test.ts +443 -0
  71. package/src/types.ts +180 -0
  72. package/tsconfig.json +12 -0
package/dist/core.js ADDED
@@ -0,0 +1,1508 @@
1
+ /**
2
+ * @fileoverview MCPWebBridge - Runtime-agnostic core for the MCP Web Bridge.
3
+ *
4
+ * This module provides the core bridge functionality that connects web frontends
5
+ * to AI agents via the Model Context Protocol (MCP). The bridge acts as an
6
+ * intermediary, handling WebSocket connections from frontends and HTTP requests
7
+ * from MCP clients.
8
+ * @module @mcp-web/bridge
9
+ */
10
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
11
+ if (kind === "m") throw new TypeError("Private method is not writable");
12
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
13
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
14
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
15
+ };
16
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
17
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
18
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
19
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
20
+ };
21
+ var _MCPWebBridge_instances, _a, _MCPWebBridge_sessions, _MCPWebBridge_queries, _MCPWebBridge_config, _MCPWebBridge_scheduler, _MCPWebBridge_tokenSessionIds, _MCPWebBridge_tokenQueryCounts, _MCPWebBridge_sessionTimeoutIntervalId, _MCPWebBridge_toolResponseHandlers, _MCPWebBridge_resourceResponseHandlers, _MCPWebBridge_mcpSessions, _MCPWebBridge_mcpSessionTimeoutIntervalId, _MCPWebBridge_resolvedIcon, _MCPWebBridge_iconReady, _MCPWebBridge_handleWebSocketConnect, _MCPWebBridge_handleWebSocketMessage, _MCPWebBridge_handleWebSocketClose, _MCPWebBridge_handleFrontendMessage, _MCPWebBridge_handleAuthentication, _MCPWebBridge_handleToolRegistration, _MCPWebBridge_handleResourceRegistration, _MCPWebBridge_handleActivity, _MCPWebBridge_handleQueryCancel, _MCPWebBridge_handleQuery, _MCPWebBridge_handleHttpRequest, _MCPWebBridge_handleQueryProgressEndpoint, _MCPWebBridge_handleQueryCompleteEndpoint, _MCPWebBridge_handleQueryFailEndpoint, _MCPWebBridge_handleQueryCancelEndpoint, _MCPWebBridge_handleMCPRequest, _MCPWebBridge_mcpSuccessResponse, _MCPWebBridge_mcpSuccessResponseWithHeaders, _MCPWebBridge_mcpErrorResponse, _MCPWebBridge_wrapToolCallResult, _MCPWebBridge_getVersion, _MCPWebBridge_resolveIcon, _MCPWebBridge_getIcon, _MCPWebBridge_handleInitialize, _MCPWebBridge_getSessionAndSessionId, _MCPWebBridge_getSessionFromMetaParams, _MCPWebBridge_createSessionNotFoundError, _MCPWebBridge_handleToolsList, _MCPWebBridge_handleToolCall, _MCPWebBridge_handleResourcesList, _MCPWebBridge_handleResourceRead, _MCPWebBridge_listSessions, _MCPWebBridge_handlePromptsList, _MCPWebBridge_forwardToolCallToSession, _MCPWebBridge_forwardResourceReadToSession, _MCPWebBridge_decrementQueryCount, _MCPWebBridge_decrementQueryCountForQuery, _MCPWebBridge_closeOldestSessionForToken, _MCPWebBridge_cleanupSession, _MCPWebBridge_startSessionTimeoutChecker, _MCPWebBridge_startMcpSessionTimeoutChecker, _MCPWebBridge_notifyToolsChanged, _MCPWebBridge_handleSSEStream, _MCPWebBridge_handleMcpSessionDelete;
22
+ import crypto from 'node:crypto';
23
+ import { readFileSync } from 'node:fs';
24
+ import { dirname, join } from 'node:path';
25
+ import { fileURLToPath, URL } from 'node:url';
26
+ import { InternalErrorCode, InvalidSessionErrorCode, McpWebConfigSchema, MissingAuthenticationErrorCode, NoSessionsFoundErrorCode, QueryAcceptedMessageSchema, QueryCancelMessageSchema, QueryCompleteBridgeMessageSchema, QueryCompleteClientMessageSchema, QueryFailureMessageSchema, QueryLimitExceededErrorCode, QueryMessageSchema, QueryNotActiveErrorCode, QueryNotFoundErrorCode, QueryProgressMessageSchema, SessionExpiredErrorCode, SessionLimitExceededErrorCode, SessionNameAlreadyInUseErrorCode, SessionNotFoundErrorCode, SessionNotSpecifiedErrorCode, ToolNameRequiredErrorCode, ToolNotAllowedErrorCode, ToolNotFoundErrorCode, ToolSchemaConflictErrorCode, UnknownMethodErrorCode, } from '@mcp-web/types';
27
+ import { NoopScheduler } from './runtime/scheduler.js';
28
+ import { jsonResponse, sseResponse } from './runtime/types.js';
29
+ const SessionNotSpecifiedErrorDetails = 'Multiple sessions available. See `available_sessions` or call the `list_sessions` tool to discover available sessions and specify the session using `_meta.sessionId`.';
30
+ // ============================================
31
+ // Helper Functions
32
+ // ============================================
33
+ /**
34
+ * Builds the query URL by appending the UUID to the agent URL.
35
+ * If no protocol is specified, defaults to http://.
36
+ */
37
+ const buildQueryUrl = (agentUrl, uuid) => {
38
+ // Add http:// if no protocol specified
39
+ const urlWithProtocol = agentUrl.includes('://') ? agentUrl : `http://${agentUrl}`;
40
+ const url = new URL(urlWithProtocol);
41
+ if (url.pathname === '/' || url.pathname === '') {
42
+ url.pathname = '/query';
43
+ }
44
+ if (url.pathname.endsWith('/')) {
45
+ url.pathname = url.pathname.slice(0, -1);
46
+ }
47
+ url.pathname = `${url.pathname}/${uuid}`;
48
+ return url.toString();
49
+ };
50
+ // ============================================
51
+ // MCPWebBridge Core Class
52
+ // ============================================
53
+ /**
54
+ * Core bridge server that connects web frontends to AI agents via MCP.
55
+ *
56
+ * MCPWebBridge manages WebSocket connections from frontends, routes tool calls,
57
+ * handles queries, and exposes an HTTP API for MCP clients. It is runtime-agnostic
58
+ * and delegates I/O operations to adapters.
59
+ *
60
+ * @example Using with Node.js adapter (recommended)
61
+ * ```typescript
62
+ * import { MCPWebBridgeNode } from '@mcp-web/bridge';
63
+ *
64
+ * const bridge = new MCPWebBridgeNode({
65
+ * name: 'My App Bridge',
66
+ * description: 'Bridge for my web application',
67
+ * });
68
+ * ```
69
+ *
70
+ * @example Using core class with custom adapter
71
+ * ```typescript
72
+ * import { MCPWebBridge } from '@mcp-web/bridge';
73
+ *
74
+ * const bridge = new MCPWebBridge(config);
75
+ * const handlers = bridge.getHandlers();
76
+ * // Wire handlers to your runtime's WebSocket/HTTP servers
77
+ * ```
78
+ */
79
+ export class MCPWebBridge {
80
+ /**
81
+ * Creates a new MCPWebBridge instance.
82
+ *
83
+ * @param config - Bridge configuration options
84
+ * @param scheduler - Optional scheduler for timing operations (used for testing)
85
+ * @throws {Error} If configuration validation fails
86
+ */
87
+ constructor(config, scheduler) {
88
+ _MCPWebBridge_instances.add(this);
89
+ _MCPWebBridge_sessions.set(this, new Map());
90
+ _MCPWebBridge_queries.set(this, new Map());
91
+ _MCPWebBridge_config.set(this, void 0);
92
+ _MCPWebBridge_scheduler.set(this, void 0);
93
+ // Session & Query limit tracking
94
+ _MCPWebBridge_tokenSessionIds.set(this, new Map());
95
+ _MCPWebBridge_tokenQueryCounts.set(this, new Map());
96
+ _MCPWebBridge_sessionTimeoutIntervalId.set(this, void 0);
97
+ // Message handlers for tool responses (keyed by requestId)
98
+ _MCPWebBridge_toolResponseHandlers.set(this, new Map());
99
+ // Message handlers for resource responses (keyed by requestId)
100
+ _MCPWebBridge_resourceResponseHandlers.set(this, new Map());
101
+ // MCP protocol sessions (Remote MCP / Streamable HTTP)
102
+ _MCPWebBridge_mcpSessions.set(this, new Map());
103
+ _MCPWebBridge_mcpSessionTimeoutIntervalId.set(this, void 0);
104
+ // Resolved icon (data URI), populated asynchronously if icon is a URL
105
+ _MCPWebBridge_resolvedIcon.set(this, void 0);
106
+ _MCPWebBridge_iconReady.set(this, void 0);
107
+ const parsedConfig = McpWebConfigSchema.safeParse(config);
108
+ if (!parsedConfig.success) {
109
+ throw new Error(`Invalid bridge server configuration: ${parsedConfig.error.message}`);
110
+ }
111
+ __classPrivateFieldSet(this, _MCPWebBridge_config, parsedConfig.data, "f");
112
+ __classPrivateFieldSet(this, _MCPWebBridge_scheduler, scheduler ?? new NoopScheduler(), "f");
113
+ // Resolve icon: if it's a URL, fetch and convert to base64 data URI
114
+ __classPrivateFieldSet(this, _MCPWebBridge_iconReady, __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_resolveIcon).call(this), "f");
115
+ // Start session timeout checker if configured
116
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").sessionMaxDurationMs) {
117
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_startSessionTimeoutChecker).call(this);
118
+ }
119
+ // Start MCP session idle timeout checker
120
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_startMcpSessionTimeoutChecker).call(this);
121
+ }
122
+ /**
123
+ * The validated bridge configuration.
124
+ * @returns The complete configuration object with defaults applied
125
+ */
126
+ get config() {
127
+ return __classPrivateFieldGet(this, _MCPWebBridge_config, "f");
128
+ }
129
+ /**
130
+ * Returns handlers for wiring to runtime-specific I/O.
131
+ *
132
+ * Use these handlers to connect the bridge to your runtime's WebSocket
133
+ * and HTTP servers. Pre-built adapters (Node, Bun, Deno, PartyKit) handle
134
+ * this automatically.
135
+ *
136
+ * @returns Object containing WebSocket and HTTP handlers
137
+ */
138
+ getHandlers() {
139
+ return {
140
+ onWebSocketConnect: (sessionId, ws, url) => __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleWebSocketConnect).call(this, sessionId, ws, url),
141
+ onWebSocketMessage: (sessionId, ws, data) => __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleWebSocketMessage).call(this, sessionId, ws, data),
142
+ onWebSocketClose: (sessionId) => __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleWebSocketClose).call(this, sessionId),
143
+ onHttpRequest: (req) => __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleHttpRequest).call(this, req),
144
+ };
145
+ }
146
+ /**
147
+ * Gracefully shuts down the bridge.
148
+ *
149
+ * Closes all WebSocket connections, cancels scheduled tasks, and clears
150
+ * all internal state. Call this when shutting down your server.
151
+ *
152
+ * @returns Promise that resolves when shutdown is complete
153
+ */
154
+ async close() {
155
+ // Stop session timeout checker
156
+ if (__classPrivateFieldGet(this, _MCPWebBridge_sessionTimeoutIntervalId, "f")) {
157
+ __classPrivateFieldGet(this, _MCPWebBridge_scheduler, "f").cancelInterval(__classPrivateFieldGet(this, _MCPWebBridge_sessionTimeoutIntervalId, "f"));
158
+ __classPrivateFieldSet(this, _MCPWebBridge_sessionTimeoutIntervalId, undefined, "f");
159
+ }
160
+ // Stop MCP session timeout checker
161
+ if (__classPrivateFieldGet(this, _MCPWebBridge_mcpSessionTimeoutIntervalId, "f")) {
162
+ __classPrivateFieldGet(this, _MCPWebBridge_scheduler, "f").cancelInterval(__classPrivateFieldGet(this, _MCPWebBridge_mcpSessionTimeoutIntervalId, "f"));
163
+ __classPrivateFieldSet(this, _MCPWebBridge_mcpSessionTimeoutIntervalId, undefined, "f");
164
+ }
165
+ // Close all WebSocket connections
166
+ for (const session of __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").values()) {
167
+ if (session.ws.readyState === 'OPEN') {
168
+ session.ws.close(1000, 'Server shutting down');
169
+ }
170
+ }
171
+ __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").clear();
172
+ // Clean up MCP sessions (close SSE streams)
173
+ for (const mcpSession of __classPrivateFieldGet(this, _MCPWebBridge_mcpSessions, "f").values()) {
174
+ if (mcpSession.sseCleanup) {
175
+ mcpSession.sseCleanup();
176
+ }
177
+ }
178
+ __classPrivateFieldGet(this, _MCPWebBridge_mcpSessions, "f").clear();
179
+ // Clear queries and tracking maps
180
+ __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").clear();
181
+ __classPrivateFieldGet(this, _MCPWebBridge_tokenSessionIds, "f").clear();
182
+ __classPrivateFieldGet(this, _MCPWebBridge_tokenQueryCounts, "f").clear();
183
+ __classPrivateFieldGet(this, _MCPWebBridge_toolResponseHandlers, "f").clear();
184
+ // Dispose scheduler
185
+ __classPrivateFieldGet(this, _MCPWebBridge_scheduler, "f").dispose();
186
+ }
187
+ }
188
+ _a = MCPWebBridge, _MCPWebBridge_sessions = new WeakMap(), _MCPWebBridge_queries = new WeakMap(), _MCPWebBridge_config = new WeakMap(), _MCPWebBridge_scheduler = new WeakMap(), _MCPWebBridge_tokenSessionIds = new WeakMap(), _MCPWebBridge_tokenQueryCounts = new WeakMap(), _MCPWebBridge_sessionTimeoutIntervalId = new WeakMap(), _MCPWebBridge_toolResponseHandlers = new WeakMap(), _MCPWebBridge_resourceResponseHandlers = new WeakMap(), _MCPWebBridge_mcpSessions = new WeakMap(), _MCPWebBridge_mcpSessionTimeoutIntervalId = new WeakMap(), _MCPWebBridge_resolvedIcon = new WeakMap(), _MCPWebBridge_iconReady = new WeakMap(), _MCPWebBridge_instances = new WeakSet(), _MCPWebBridge_handleWebSocketConnect = function _MCPWebBridge_handleWebSocketConnect(_sessionId, _ws, url) {
189
+ const session = url.searchParams.get('session');
190
+ if (!session) {
191
+ return false;
192
+ }
193
+ return true;
194
+ }, _MCPWebBridge_handleWebSocketMessage = function _MCPWebBridge_handleWebSocketMessage(sessionId, ws, data) {
195
+ try {
196
+ const message = JSON.parse(data);
197
+ // Check if this is a tool response
198
+ if (message.type === 'tool-response') {
199
+ const handler = __classPrivateFieldGet(this, _MCPWebBridge_toolResponseHandlers, "f").get(message.requestId);
200
+ if (handler) {
201
+ handler(data);
202
+ return;
203
+ }
204
+ }
205
+ // Check if this is a resource response
206
+ if (message.type === 'resource-response') {
207
+ const handler = __classPrivateFieldGet(this, _MCPWebBridge_resourceResponseHandlers, "f").get(message.requestId);
208
+ if (handler) {
209
+ handler(data);
210
+ return;
211
+ }
212
+ }
213
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleFrontendMessage).call(this, sessionId, message, ws);
214
+ }
215
+ catch (error) {
216
+ console.error('Invalid JSON message:', error);
217
+ ws.close(1003, 'Invalid JSON');
218
+ }
219
+ }, _MCPWebBridge_handleWebSocketClose = function _MCPWebBridge_handleWebSocketClose(sessionId) {
220
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_cleanupSession).call(this, sessionId);
221
+ }, _MCPWebBridge_handleFrontendMessage = function _MCPWebBridge_handleFrontendMessage(sessionId, message, ws) {
222
+ switch (message.type) {
223
+ case 'authenticate':
224
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleAuthentication).call(this, sessionId, message, ws);
225
+ break;
226
+ case 'register-tool':
227
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleToolRegistration).call(this, sessionId, message);
228
+ break;
229
+ case 'register-resource':
230
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleResourceRegistration).call(this, sessionId, message);
231
+ break;
232
+ case 'activity':
233
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleActivity).call(this, sessionId, message);
234
+ break;
235
+ case 'tool-response':
236
+ // Handled by per-request listeners
237
+ break;
238
+ case 'resource-response':
239
+ // Handled by per-request listeners
240
+ break;
241
+ case 'query':
242
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleQuery).call(this, sessionId, message, ws);
243
+ break;
244
+ case 'query_cancel':
245
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleQueryCancel).call(this, message);
246
+ break;
247
+ default:
248
+ console.warn(`Unknown message type: ${message.type}`);
249
+ }
250
+ }, _MCPWebBridge_handleAuthentication = function _MCPWebBridge_handleAuthentication(sessionId, message, ws) {
251
+ const { authToken } = message;
252
+ // Check session limit
253
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").maxSessionsPerToken) {
254
+ const existingSessions = __classPrivateFieldGet(this, _MCPWebBridge_tokenSessionIds, "f").get(authToken);
255
+ const currentCount = existingSessions?.size ?? 0;
256
+ if (currentCount >= __classPrivateFieldGet(this, _MCPWebBridge_config, "f").maxSessionsPerToken) {
257
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").onSessionLimitExceeded === 'close_oldest') {
258
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_closeOldestSessionForToken).call(this, authToken);
259
+ }
260
+ else {
261
+ ws.send(JSON.stringify({
262
+ type: 'authentication-failed',
263
+ error: 'Session limit exceeded',
264
+ code: SessionLimitExceededErrorCode,
265
+ }));
266
+ ws.close(1008, 'Session limit exceeded');
267
+ return;
268
+ }
269
+ }
270
+ }
271
+ // Check for duplicate session name
272
+ if (message.sessionName) {
273
+ const existingSessionIds = __classPrivateFieldGet(this, _MCPWebBridge_tokenSessionIds, "f").get(authToken);
274
+ if (existingSessionIds) {
275
+ for (const existingId of existingSessionIds) {
276
+ const existingSession = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(existingId);
277
+ if (existingSession?.sessionName === message.sessionName) {
278
+ ws.send(JSON.stringify({
279
+ type: 'authentication-failed',
280
+ error: `Session name "${message.sessionName}" is already in use`,
281
+ code: SessionNameAlreadyInUseErrorCode,
282
+ }));
283
+ ws.close(1008, 'Session name already in use');
284
+ return;
285
+ }
286
+ }
287
+ }
288
+ }
289
+ const sessionData = {
290
+ ws,
291
+ authToken: message.authToken,
292
+ origin: message.origin,
293
+ pageTitle: message.pageTitle,
294
+ sessionName: message.sessionName,
295
+ userAgent: message.userAgent,
296
+ connectedAt: Date.now(),
297
+ lastActivity: Date.now(),
298
+ tools: new Map(),
299
+ resources: new Map(),
300
+ };
301
+ __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").set(sessionId, sessionData);
302
+ // Track session for this token
303
+ const sessionIds = __classPrivateFieldGet(this, _MCPWebBridge_tokenSessionIds, "f").get(authToken) ?? new Set();
304
+ sessionIds.add(sessionId);
305
+ __classPrivateFieldGet(this, _MCPWebBridge_tokenSessionIds, "f").set(authToken, sessionIds);
306
+ ws.send(JSON.stringify({
307
+ type: 'authenticated',
308
+ sessionId,
309
+ success: true,
310
+ }));
311
+ }, _MCPWebBridge_handleToolRegistration = function _MCPWebBridge_handleToolRegistration(sessionId, message) {
312
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(sessionId);
313
+ if (!session) {
314
+ console.warn(`Tool registration for unknown session: ${sessionId}`);
315
+ return;
316
+ }
317
+ const toolName = message.tool.name;
318
+ const newSchema = JSON.stringify(message.tool.inputSchema ?? {});
319
+ // Check sibling sessions (same auth token) for schema conflicts
320
+ const siblingSessionIds = __classPrivateFieldGet(this, _MCPWebBridge_tokenSessionIds, "f").get(session.authToken);
321
+ if (siblingSessionIds) {
322
+ for (const siblingId of siblingSessionIds) {
323
+ if (siblingId === sessionId)
324
+ continue;
325
+ const sibling = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(siblingId);
326
+ if (!sibling)
327
+ continue;
328
+ const existingTool = sibling.tools.get(toolName);
329
+ if (existingTool) {
330
+ const existingSchema = JSON.stringify(existingTool.inputSchema ?? {});
331
+ if (existingSchema !== newSchema) {
332
+ console.warn(`Tool schema conflict: '${toolName}' registered by session ${siblingId} has a different schema. Rejecting registration from session ${sessionId}.`);
333
+ session.ws.send(JSON.stringify({
334
+ type: 'tool-registration-error',
335
+ toolName,
336
+ error: ToolSchemaConflictErrorCode,
337
+ message: `Tool '${toolName}' is already registered by another session with a different schema. Tools with the same name must have identical schemas across sessions.`,
338
+ }));
339
+ return;
340
+ }
341
+ }
342
+ }
343
+ }
344
+ console.log('registering tool for session', sessionId, message);
345
+ session.tools.set(message.tool.name, message.tool);
346
+ // Notify connected MCP clients (Claude Desktop) about tool changes
347
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_notifyToolsChanged).call(this, session.authToken);
348
+ }, _MCPWebBridge_handleResourceRegistration = function _MCPWebBridge_handleResourceRegistration(sessionId, message) {
349
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(sessionId);
350
+ if (!session) {
351
+ console.warn(`Resource registration for unknown session: ${sessionId}`);
352
+ return;
353
+ }
354
+ console.log('registering resource for session', sessionId, message);
355
+ session.resources.set(message.resource.uri, message.resource);
356
+ }, _MCPWebBridge_handleActivity = function _MCPWebBridge_handleActivity(sessionId, message) {
357
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(sessionId);
358
+ if (session) {
359
+ session.lastActivity = message.timestamp;
360
+ }
361
+ }, _MCPWebBridge_handleQueryCancel = async function _MCPWebBridge_handleQueryCancel(message) {
362
+ const cancelMessage = QueryCancelMessageSchema.parse(message);
363
+ const { uuid } = cancelMessage;
364
+ const query = __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").get(uuid);
365
+ if (!query) {
366
+ console.warn(`Cancel requested for unknown query: ${uuid}`);
367
+ return;
368
+ }
369
+ query.state = 'cancelled';
370
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").agentUrl) {
371
+ try {
372
+ await fetch(buildQueryUrl(__classPrivateFieldGet(this, _MCPWebBridge_config, "f").agentUrl, uuid), {
373
+ method: 'DELETE',
374
+ headers: { 'Content-Type': 'application/json' },
375
+ });
376
+ }
377
+ catch (error) {
378
+ console.debug(`Failed to notify agent of query deletion (optional): ${error instanceof Error ? error.message : String(error)}`);
379
+ }
380
+ }
381
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_decrementQueryCountForQuery).call(this, query);
382
+ __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").delete(uuid);
383
+ }, _MCPWebBridge_handleQuery = async function _MCPWebBridge_handleQuery(sessionId, message, ws) {
384
+ const { uuid, responseTool, tools, restrictTools } = message;
385
+ if (!__classPrivateFieldGet(this, _MCPWebBridge_config, "f").agentUrl) {
386
+ ws.send(JSON.stringify(QueryFailureMessageSchema.parse({
387
+ uuid,
388
+ error: 'Missing Agent URL',
389
+ })));
390
+ return;
391
+ }
392
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(sessionId);
393
+ if (!session) {
394
+ ws.send(JSON.stringify(QueryFailureMessageSchema.parse({
395
+ uuid,
396
+ error: 'Session not found',
397
+ })));
398
+ return;
399
+ }
400
+ // Check query limit
401
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").maxInFlightQueriesPerToken) {
402
+ const currentQueries = __classPrivateFieldGet(this, _MCPWebBridge_tokenQueryCounts, "f").get(session.authToken) ?? 0;
403
+ if (currentQueries >= __classPrivateFieldGet(this, _MCPWebBridge_config, "f").maxInFlightQueriesPerToken) {
404
+ ws.send(JSON.stringify(QueryFailureMessageSchema.parse({
405
+ uuid,
406
+ error: 'Query limit exceeded. Wait for existing queries to complete.',
407
+ code: QueryLimitExceededErrorCode,
408
+ })));
409
+ return;
410
+ }
411
+ }
412
+ // Increment query count
413
+ __classPrivateFieldGet(this, _MCPWebBridge_tokenQueryCounts, "f").set(session.authToken, (__classPrivateFieldGet(this, _MCPWebBridge_tokenQueryCounts, "f").get(session.authToken) ?? 0) + 1);
414
+ try {
415
+ __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").set(uuid, {
416
+ sessionId,
417
+ responseTool: responseTool?.name,
418
+ toolCalls: [],
419
+ ws,
420
+ state: 'active',
421
+ tools,
422
+ restrictTools,
423
+ });
424
+ const response = await fetch(buildQueryUrl(__classPrivateFieldGet(this, _MCPWebBridge_config, "f").agentUrl, uuid), {
425
+ method: 'PUT',
426
+ headers: {
427
+ 'Content-Type': 'application/json',
428
+ ...(__classPrivateFieldGet(this, _MCPWebBridge_config, "f").authToken && {
429
+ Authorization: `Bearer ${__classPrivateFieldGet(this, _MCPWebBridge_config, "f").authToken}`,
430
+ }),
431
+ },
432
+ body: JSON.stringify(QueryMessageSchema.parse(message)),
433
+ });
434
+ if (!response.ok) {
435
+ throw new Error(`Agent responded with ${response.status}: ${response.statusText}`);
436
+ }
437
+ ws.send(JSON.stringify(QueryAcceptedMessageSchema.parse({ uuid })));
438
+ }
439
+ catch (error) {
440
+ console.error(`Error forwarding query ${uuid}:`, error);
441
+ __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").delete(uuid);
442
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_decrementQueryCount).call(this, session.authToken);
443
+ ws.send(JSON.stringify(QueryFailureMessageSchema.parse({
444
+ uuid,
445
+ error: `${error instanceof Error ? error.message : String(error)}`,
446
+ })));
447
+ }
448
+ }, _MCPWebBridge_handleHttpRequest =
449
+ // ============================================
450
+ // HTTP Request Handling
451
+ // ============================================
452
+ async function _MCPWebBridge_handleHttpRequest(req) {
453
+ const startTime = Date.now();
454
+ // Debug logging helper
455
+ const debug = (message, data) => {
456
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").debug) {
457
+ if (data !== undefined) {
458
+ console.log(`[MCP Debug] ${message}`, data);
459
+ }
460
+ else {
461
+ console.log(`[MCP Debug] ${message}`);
462
+ }
463
+ }
464
+ };
465
+ debug(`→ ${req.method} ${req.url}`);
466
+ debug(` Headers:`, {
467
+ accept: req.headers.get('accept'),
468
+ contentType: req.headers.get('content-type'),
469
+ authorization: req.headers.get('authorization') ? '[PRESENT]' : '[ABSENT]',
470
+ mcpSessionId: req.headers.get('mcp-session-id'),
471
+ });
472
+ // Handle CORS preflight
473
+ if (req.method === 'OPTIONS') {
474
+ debug(`← 200 (CORS preflight)`);
475
+ return jsonResponse(200, '');
476
+ }
477
+ const url = new URL(req.url, 'http://localhost');
478
+ const pathname = url.pathname;
479
+ // Route query endpoints
480
+ const queryProgressMatch = pathname.match(/^\/query\/([^/]+)\/progress$/);
481
+ const queryCompleteMatch = pathname.match(/^\/query\/([^/]+)\/complete$/);
482
+ const queryFailMatch = pathname.match(/^\/query\/([^/]+)\/fail$/);
483
+ const queryCancelMatch = pathname.match(/^\/query\/([^/]+)\/cancel$/);
484
+ if (req.method === 'POST' && queryProgressMatch) {
485
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleQueryProgressEndpoint).call(this, queryProgressMatch[1], req);
486
+ }
487
+ if (req.method === 'PUT' && queryCompleteMatch) {
488
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleQueryCompleteEndpoint).call(this, queryCompleteMatch[1], req);
489
+ }
490
+ if (req.method === 'PUT' && queryFailMatch) {
491
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleQueryFailEndpoint).call(this, queryFailMatch[1], req);
492
+ }
493
+ if (req.method === 'PUT' && queryCancelMatch) {
494
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleQueryCancelEndpoint).call(this, queryCancelMatch[1], req);
495
+ }
496
+ // Handle MCP session deletion (client closing session)
497
+ if (req.method === 'DELETE') {
498
+ const mcpSessionId = req.headers.get('mcp-session-id');
499
+ if (mcpSessionId) {
500
+ debug(` Processing DELETE for session ${mcpSessionId}`);
501
+ const response = __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleMcpSessionDelete).call(this, mcpSessionId);
502
+ debug(`← ${response.status} (session delete) [${Date.now() - startTime}ms]`);
503
+ return response;
504
+ }
505
+ debug(`← 400 (missing Mcp-Session-Id) [${Date.now() - startTime}ms]`);
506
+ return jsonResponse(400, { error: 'Mcp-Session-Id header required' });
507
+ }
508
+ // Handle GET requests for SSE stream (Remote MCP server-initiated messages)
509
+ if (req.method === 'GET') {
510
+ const acceptsSSE = req.headers.get('accept')?.includes('text/event-stream');
511
+ if (acceptsSSE) {
512
+ debug(` Opening SSE stream`);
513
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleSSEStream).call(this, req);
514
+ }
515
+ // Plain GET returns server info (no auth required)
516
+ debug(`← 200 (server info) [${Date.now() - startTime}ms]`);
517
+ const icon = await __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_getIcon).call(this);
518
+ return jsonResponse(200, {
519
+ name: __classPrivateFieldGet(this, _MCPWebBridge_config, "f").name,
520
+ description: __classPrivateFieldGet(this, _MCPWebBridge_config, "f").description,
521
+ version: __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_getVersion).call(this),
522
+ ...(icon && { icon }),
523
+ });
524
+ }
525
+ // Handle MCP JSON-RPC requests
526
+ if (req.method === 'POST') {
527
+ debug(` Processing MCP request`);
528
+ const response = await __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleMCPRequest).call(this, req);
529
+ debug(`← ${response.status} [${Date.now() - startTime}ms]`);
530
+ return response;
531
+ }
532
+ debug(`← 404 (not found) [${Date.now() - startTime}ms]`);
533
+ return jsonResponse(404, { error: 'Not Found' });
534
+ }, _MCPWebBridge_handleQueryProgressEndpoint = async function _MCPWebBridge_handleQueryProgressEndpoint(uuid, req) {
535
+ try {
536
+ const body = await req.text();
537
+ const message = JSON.parse(body);
538
+ const progressMessage = QueryProgressMessageSchema.parse({
539
+ uuid,
540
+ ...message,
541
+ });
542
+ const query = __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").get(uuid);
543
+ if (!query) {
544
+ return jsonResponse(404, { error: QueryNotFoundErrorCode });
545
+ }
546
+ if (query.ws.readyState === 'OPEN') {
547
+ query.ws.send(JSON.stringify(progressMessage));
548
+ }
549
+ return jsonResponse(200, { success: true });
550
+ }
551
+ catch (error) {
552
+ console.error('Error handling query progress:', error);
553
+ return jsonResponse(400, { error: 'Invalid request body' });
554
+ }
555
+ }, _MCPWebBridge_handleQueryCompleteEndpoint = async function _MCPWebBridge_handleQueryCompleteEndpoint(uuid, req) {
556
+ try {
557
+ const body = await req.text();
558
+ const message = JSON.parse(body);
559
+ const completeMessage = QueryCompleteClientMessageSchema.parse({
560
+ uuid,
561
+ ...message,
562
+ });
563
+ const query = __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").get(uuid);
564
+ if (!query) {
565
+ return jsonResponse(404, { error: QueryNotFoundErrorCode });
566
+ }
567
+ if (query.responseTool) {
568
+ const errorMessage = QueryFailureMessageSchema.parse({
569
+ uuid,
570
+ error: `Query specified responseTool '${query.responseTool}' but agent called queryComplete() instead`,
571
+ });
572
+ if (query.ws.readyState === 'OPEN') {
573
+ query.ws.send(JSON.stringify(errorMessage));
574
+ }
575
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_decrementQueryCountForQuery).call(this, query);
576
+ __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").delete(uuid);
577
+ return jsonResponse(400, { error: errorMessage.error });
578
+ }
579
+ query.state = 'completed';
580
+ const bridgeMessage = QueryCompleteBridgeMessageSchema.parse({
581
+ uuid,
582
+ message: completeMessage.message,
583
+ toolCalls: query.toolCalls,
584
+ });
585
+ if (query.ws.readyState === 'OPEN') {
586
+ query.ws.send(JSON.stringify(bridgeMessage));
587
+ }
588
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_decrementQueryCountForQuery).call(this, query);
589
+ __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").delete(uuid);
590
+ return jsonResponse(200, { success: true });
591
+ }
592
+ catch (error) {
593
+ console.error('Error handling query complete:', error);
594
+ return jsonResponse(400, { error: 'Invalid request body' });
595
+ }
596
+ }, _MCPWebBridge_handleQueryFailEndpoint = async function _MCPWebBridge_handleQueryFailEndpoint(uuid, req) {
597
+ try {
598
+ const body = await req.text();
599
+ const message = JSON.parse(body);
600
+ const failureMessage = QueryFailureMessageSchema.parse({
601
+ uuid,
602
+ ...message,
603
+ });
604
+ const query = __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").get(uuid);
605
+ if (!query) {
606
+ return jsonResponse(404, { error: QueryNotFoundErrorCode });
607
+ }
608
+ query.state = 'failed';
609
+ if (query.ws.readyState === 'OPEN') {
610
+ query.ws.send(JSON.stringify(failureMessage));
611
+ }
612
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_decrementQueryCountForQuery).call(this, query);
613
+ __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").delete(uuid);
614
+ return jsonResponse(200, { success: true });
615
+ }
616
+ catch (error) {
617
+ console.error('Error handling query fail:', error);
618
+ return jsonResponse(400, { error: 'Invalid request body' });
619
+ }
620
+ }, _MCPWebBridge_handleQueryCancelEndpoint = async function _MCPWebBridge_handleQueryCancelEndpoint(uuid, req) {
621
+ try {
622
+ const query = __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").get(uuid);
623
+ if (!query) {
624
+ return jsonResponse(404, { error: QueryNotFoundErrorCode });
625
+ }
626
+ query.state = 'cancelled';
627
+ const body = await req.text();
628
+ const cancellationMessage = QueryCancelMessageSchema.parse({
629
+ uuid,
630
+ reason: body ? JSON.parse(body).reason : undefined,
631
+ });
632
+ if (query.ws.readyState === 'OPEN') {
633
+ query.ws.send(JSON.stringify(cancellationMessage));
634
+ }
635
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_decrementQueryCountForQuery).call(this, query);
636
+ __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").delete(uuid);
637
+ return jsonResponse(200, { success: true });
638
+ }
639
+ catch (error) {
640
+ console.error('Error handling query cancel:', error);
641
+ return jsonResponse(400, { error: 'Invalid request body' });
642
+ }
643
+ }, _MCPWebBridge_handleMCPRequest =
644
+ // ============================================
645
+ // MCP JSON-RPC Handling
646
+ // ============================================
647
+ async function _MCPWebBridge_handleMCPRequest(req) {
648
+ try {
649
+ const body = await req.text();
650
+ const mcpRequest = JSON.parse(body);
651
+ // Debug logging
652
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").debug) {
653
+ console.log(`[MCP Debug] Method: ${mcpRequest.method}, ID: ${mcpRequest.id}`);
654
+ if (mcpRequest.params && Object.keys(mcpRequest.params).length > 0) {
655
+ console.log(`[MCP Debug] Params:`, JSON.stringify(mcpRequest.params).substring(0, 200));
656
+ }
657
+ }
658
+ // Extract auth token from header OR URL query param (for Remote MCP compatibility)
659
+ const authHeader = req.headers.get('authorization');
660
+ const url = new URL(req.url, 'http://localhost');
661
+ const authToken = authHeader?.replace('Bearer ', '') ?? url.searchParams.get('token') ?? undefined;
662
+ const mcpSessionId = req.headers.get('mcp-session-id');
663
+ const queryId = mcpRequest.params?._meta?.queryId;
664
+ // Handle initialize separately - it creates an MCP session
665
+ if (mcpRequest.method === 'initialize') {
666
+ if (!authToken) {
667
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").debug) {
668
+ console.log(`[MCP Debug] Error: Missing authentication token`);
669
+ }
670
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_mcpErrorResponse).call(this, mcpRequest.id, -32600, MissingAuthenticationErrorCode);
671
+ }
672
+ const { result, sessionId } = await __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleInitialize).call(this, authToken);
673
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").debug) {
674
+ console.log(`[MCP Debug] Created MCP session: ${sessionId}`);
675
+ }
676
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_mcpSuccessResponseWithHeaders).call(this, mcpRequest.id, result, {
677
+ 'Mcp-Session-Id': sessionId,
678
+ });
679
+ }
680
+ // Handle initialized notification (no response needed per spec, but we accept it)
681
+ if (mcpRequest.method === 'notifications/initialized') {
682
+ // Update MCP session activity
683
+ if (mcpSessionId) {
684
+ const mcpSession = __classPrivateFieldGet(this, _MCPWebBridge_mcpSessions, "f").get(mcpSessionId);
685
+ if (mcpSession) {
686
+ mcpSession.lastActivity = Date.now();
687
+ }
688
+ }
689
+ return jsonResponse(202, '');
690
+ }
691
+ // For all other requests, validate MCP session if provided
692
+ if (mcpSessionId) {
693
+ const mcpSession = __classPrivateFieldGet(this, _MCPWebBridge_mcpSessions, "f").get(mcpSessionId);
694
+ if (!mcpSession) {
695
+ return jsonResponse(404, { error: 'MCP session not found' });
696
+ }
697
+ mcpSession.lastActivity = Date.now();
698
+ }
699
+ const sessions = new Map();
700
+ if (queryId) {
701
+ const query = __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").get(queryId);
702
+ if (!query) {
703
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_mcpErrorResponse).call(this, mcpRequest.id, -32600, QueryNotFoundErrorCode);
704
+ }
705
+ if (query.state !== 'active') {
706
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_mcpErrorResponse).call(this, mcpRequest.id, -32600, QueryNotActiveErrorCode);
707
+ }
708
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(query.sessionId);
709
+ if (!session) {
710
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_mcpErrorResponse).call(this, mcpRequest.id, -32600, InvalidSessionErrorCode);
711
+ }
712
+ sessions.set(query.sessionId, session);
713
+ }
714
+ else if (authToken) {
715
+ for (const [sessionId, session] of __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").entries()) {
716
+ if (session.authToken === authToken) {
717
+ sessions.set(sessionId, session);
718
+ }
719
+ }
720
+ }
721
+ else {
722
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_mcpErrorResponse).call(this, mcpRequest.id, -32600, MissingAuthenticationErrorCode);
723
+ }
724
+ if (sessions.size === 0) {
725
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_mcpErrorResponse).call(this, mcpRequest.id, -32600, NoSessionsFoundErrorCode);
726
+ }
727
+ let result;
728
+ switch (mcpRequest.method) {
729
+ case 'tools/list':
730
+ result = await __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleToolsList).call(this, sessions, mcpRequest.params);
731
+ break;
732
+ case 'tools/call':
733
+ result = await __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleToolCall).call(this, sessions, mcpRequest.params);
734
+ result = __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_wrapToolCallResult).call(this, result);
735
+ break;
736
+ case 'resources/list':
737
+ result = await __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleResourcesList).call(this, sessions, mcpRequest.params);
738
+ break;
739
+ case 'resources/read':
740
+ result = await __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handleResourceRead).call(this, sessions, mcpRequest.params);
741
+ break;
742
+ case 'prompts/list':
743
+ result = await __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_handlePromptsList).call(this, sessions, mcpRequest.params);
744
+ break;
745
+ default:
746
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_mcpErrorResponse).call(this, mcpRequest.id, -32601, UnknownMethodErrorCode);
747
+ }
748
+ // Check for fatal errors
749
+ if (result &&
750
+ typeof result === 'object' &&
751
+ 'error_is_fatal' in result &&
752
+ result.error_is_fatal === true) {
753
+ const fatalError = result;
754
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_mcpErrorResponse).call(this, mcpRequest.id, -32602, fatalError.error_message, fatalError);
755
+ }
756
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_mcpSuccessResponse).call(this, mcpRequest.id, result);
757
+ }
758
+ catch (error) {
759
+ console.error('MCP request error:', error);
760
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_mcpErrorResponse).call(this, 0, -32603, InternalErrorCode);
761
+ }
762
+ }, _MCPWebBridge_mcpSuccessResponse = function _MCPWebBridge_mcpSuccessResponse(id, result) {
763
+ const response = {
764
+ jsonrpc: '2.0',
765
+ id,
766
+ result,
767
+ };
768
+ return jsonResponse(200, response);
769
+ }, _MCPWebBridge_mcpSuccessResponseWithHeaders = function _MCPWebBridge_mcpSuccessResponseWithHeaders(id, result, headers) {
770
+ const response = {
771
+ jsonrpc: '2.0',
772
+ id,
773
+ result,
774
+ };
775
+ return {
776
+ status: 200,
777
+ headers: {
778
+ 'content-type': 'application/json',
779
+ ...headers,
780
+ },
781
+ body: JSON.stringify(response),
782
+ };
783
+ }, _MCPWebBridge_mcpErrorResponse = function _MCPWebBridge_mcpErrorResponse(id, code, message, data) {
784
+ const response = {
785
+ jsonrpc: '2.0',
786
+ id,
787
+ error: { code, message, data },
788
+ };
789
+ return jsonResponse(200, response);
790
+ }, _MCPWebBridge_wrapToolCallResult = function _MCPWebBridge_wrapToolCallResult(result) {
791
+ // Check if this is an error response
792
+ if (result && typeof result === 'object' && 'error' in result) {
793
+ return {
794
+ content: [
795
+ {
796
+ type: 'text',
797
+ text: JSON.stringify(result, null, 2),
798
+ },
799
+ ],
800
+ isError: true,
801
+ };
802
+ }
803
+ // Handle different result types
804
+ if (typeof result === 'string') {
805
+ // Check if it's a data URL (image)
806
+ if (result.startsWith('data:image/')) {
807
+ const mimeType = result.split(';')[0].split(':')[1];
808
+ // Extract base64 data after the comma
809
+ const base64Data = result.split(',')[1];
810
+ return {
811
+ content: [
812
+ {
813
+ type: 'image',
814
+ data: base64Data,
815
+ mimeType,
816
+ },
817
+ ],
818
+ };
819
+ }
820
+ return {
821
+ content: [
822
+ {
823
+ type: 'text',
824
+ text: result,
825
+ },
826
+ ],
827
+ };
828
+ }
829
+ if (result !== null && result !== undefined) {
830
+ // Check if it's an object containing a data URL (e.g., { dataUrl: "data:image/png;base64,..." })
831
+ // This handles tools that return image data wrapped in an object rather than as a raw string.
832
+ if (typeof result === 'object' && 'dataUrl' in result) {
833
+ const dataUrl = result.dataUrl;
834
+ if (typeof dataUrl === 'string' && dataUrl.startsWith('data:image/')) {
835
+ const mimeType = dataUrl.split(';')[0].split(':')[1];
836
+ const base64Data = dataUrl.split(',')[1];
837
+ return {
838
+ content: [
839
+ {
840
+ type: 'image',
841
+ data: base64Data,
842
+ mimeType,
843
+ },
844
+ ],
845
+ };
846
+ }
847
+ }
848
+ // Extract _meta from the result object to place at the top level of CallToolResult.
849
+ // The MCP protocol expects _meta as a top-level field on the result object,
850
+ // not embedded inside the JSON text content (where the host can't find it).
851
+ let topLevelMeta;
852
+ let resultToSerialize = result;
853
+ if (typeof result === 'object' && '_meta' in result) {
854
+ const { _meta, ...rest } = result;
855
+ if (_meta && typeof _meta === 'object') {
856
+ topLevelMeta = _meta;
857
+ }
858
+ resultToSerialize = rest;
859
+ }
860
+ const wrapped = {
861
+ content: [
862
+ {
863
+ type: 'text',
864
+ text: typeof resultToSerialize === 'object' ? JSON.stringify(resultToSerialize, null, 2) : String(resultToSerialize),
865
+ },
866
+ ],
867
+ };
868
+ if (topLevelMeta) {
869
+ wrapped._meta = topLevelMeta;
870
+ }
871
+ return wrapped;
872
+ }
873
+ // null or undefined result
874
+ return {
875
+ content: [
876
+ {
877
+ type: 'text',
878
+ text: '',
879
+ },
880
+ ],
881
+ };
882
+ }, _MCPWebBridge_getVersion = function _MCPWebBridge_getVersion() {
883
+ try {
884
+ const __filename = fileURLToPath(import.meta.url);
885
+ const __dirname = dirname(__filename);
886
+ const packageJsonPath = join(__dirname, '..', 'package.json');
887
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
888
+ return packageJson.version || '1.0.0';
889
+ }
890
+ catch {
891
+ return '1.0.0';
892
+ }
893
+ }, _MCPWebBridge_resolveIcon =
894
+ /**
895
+ * Resolves the icon config value to a data URI.
896
+ * If icon is already a data URI, uses it directly.
897
+ * If icon is an HTTP(S) URL, fetches it and converts to a base64 data URI.
898
+ * Fails gracefully — icon is simply omitted if resolution fails.
899
+ */
900
+ async function _MCPWebBridge_resolveIcon() {
901
+ const icon = __classPrivateFieldGet(this, _MCPWebBridge_config, "f").icon;
902
+ if (!icon)
903
+ return;
904
+ // Already a data URI — use as-is
905
+ if (icon.startsWith('data:')) {
906
+ __classPrivateFieldSet(this, _MCPWebBridge_resolvedIcon, icon, "f");
907
+ return;
908
+ }
909
+ // HTTP(S) URL — fetch and convert
910
+ if (icon.startsWith('http://') || icon.startsWith('https://')) {
911
+ try {
912
+ const response = await fetch(icon);
913
+ if (!response.ok) {
914
+ console.warn(`[MCPWebBridge] Failed to fetch icon from ${icon}: HTTP ${response.status}`);
915
+ return;
916
+ }
917
+ const contentType = response.headers.get('content-type') || 'image/png';
918
+ const buffer = await response.arrayBuffer();
919
+ const base64 = Buffer.from(buffer).toString('base64');
920
+ __classPrivateFieldSet(this, _MCPWebBridge_resolvedIcon, `data:${contentType};base64,${base64}`, "f");
921
+ }
922
+ catch (error) {
923
+ console.warn(`[MCPWebBridge] Failed to fetch icon from ${icon}:`, error instanceof Error ? error.message : error);
924
+ }
925
+ return;
926
+ }
927
+ // Unrecognized format — use as-is (could be a relative URL)
928
+ __classPrivateFieldSet(this, _MCPWebBridge_resolvedIcon, icon, "f");
929
+ }, _MCPWebBridge_getIcon =
930
+ /**
931
+ * Returns the resolved icon data URI, waiting for resolution if needed.
932
+ */
933
+ async function _MCPWebBridge_getIcon() {
934
+ await __classPrivateFieldGet(this, _MCPWebBridge_iconReady, "f");
935
+ return __classPrivateFieldGet(this, _MCPWebBridge_resolvedIcon, "f");
936
+ }, _MCPWebBridge_handleInitialize =
937
+ /**
938
+ * Handles MCP initialize request and creates a new MCP session.
939
+ * Returns the initialize result along with a session ID for the Mcp-Session-Id header.
940
+ */
941
+ async function _MCPWebBridge_handleInitialize(authToken) {
942
+ // Create a new MCP session
943
+ const sessionId = crypto.randomUUID();
944
+ const mcpSession = {
945
+ id: sessionId,
946
+ authToken,
947
+ createdAt: Date.now(),
948
+ lastActivity: Date.now(),
949
+ };
950
+ __classPrivateFieldGet(this, _MCPWebBridge_mcpSessions, "f").set(sessionId, mcpSession);
951
+ const icon = await __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_getIcon).call(this);
952
+ const result = {
953
+ protocolVersion: '2024-11-05',
954
+ capabilities: {
955
+ tools: { listChanged: true },
956
+ resources: {},
957
+ prompts: {},
958
+ },
959
+ serverInfo: {
960
+ name: __classPrivateFieldGet(this, _MCPWebBridge_config, "f").name,
961
+ description: __classPrivateFieldGet(this, _MCPWebBridge_config, "f").description,
962
+ version: __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_getVersion).call(this),
963
+ ...(icon && { icon }),
964
+ },
965
+ };
966
+ return { result, sessionId };
967
+ }, _MCPWebBridge_getSessionAndSessionId = function _MCPWebBridge_getSessionAndSessionId(sessions, sessionId) {
968
+ if (!sessionId) {
969
+ if (sessions.size === 1) {
970
+ sessionId = sessions.keys().next().value;
971
+ if (!sessionId) {
972
+ return undefined;
973
+ }
974
+ }
975
+ else {
976
+ return undefined;
977
+ }
978
+ }
979
+ const session = sessions.get(sessionId);
980
+ if (!session) {
981
+ return undefined;
982
+ }
983
+ return [sessionId, session];
984
+ }, _MCPWebBridge_getSessionFromMetaParams = function _MCPWebBridge_getSessionFromMetaParams(sessions, params) {
985
+ const sessionId = params?._meta?.sessionId;
986
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_getSessionAndSessionId).call(this, sessions, sessionId)?.[1];
987
+ }, _MCPWebBridge_createSessionNotFoundError = function _MCPWebBridge_createSessionNotFoundError(sessions) {
988
+ if (sessions.size > 1) {
989
+ return {
990
+ error: SessionNotSpecifiedErrorCode,
991
+ error_message: SessionNotSpecifiedErrorDetails,
992
+ available_sessions: __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_listSessions).call(this, sessions),
993
+ };
994
+ }
995
+ return { error: SessionNotFoundErrorCode };
996
+ }, _MCPWebBridge_handleToolsList = async function _MCPWebBridge_handleToolsList(sessions, params) {
997
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_getSessionFromMetaParams).call(this, sessions, params);
998
+ const listSessionsTool = {
999
+ name: 'list_sessions',
1000
+ description: 'List all browser sessions with their available tools',
1001
+ inputSchema: {
1002
+ type: 'object',
1003
+ properties: {},
1004
+ required: [],
1005
+ },
1006
+ };
1007
+ if (!session && sessions.size > 1) {
1008
+ // Multiple sessions: expose all tools (deduplicated) with session_id required
1009
+ const tools = [listSessionsTool];
1010
+ const seen = new Set();
1011
+ for (const s of sessions.values()) {
1012
+ for (const tool of s.tools.values()) {
1013
+ if (seen.has(tool.name))
1014
+ continue;
1015
+ seen.add(tool.name);
1016
+ tools.push({
1017
+ name: tool.name,
1018
+ description: tool.description,
1019
+ inputSchema: {
1020
+ type: 'object',
1021
+ properties: {
1022
+ session_id: {
1023
+ type: 'string',
1024
+ description: 'Session ID (required) — use list_sessions to see available sessions',
1025
+ },
1026
+ ...(tool.inputSchema?.properties || {}),
1027
+ },
1028
+ required: [
1029
+ 'session_id',
1030
+ ...(tool.inputSchema?.required || []),
1031
+ ],
1032
+ },
1033
+ // Forward _meta (e.g., _meta.ui.resourceUri for MCP Apps)
1034
+ ...(tool._meta ? { _meta: tool._meta } : {}),
1035
+ });
1036
+ }
1037
+ }
1038
+ return {
1039
+ tools,
1040
+ _meta: { available_sessions: __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_listSessions).call(this, sessions) },
1041
+ };
1042
+ }
1043
+ if (!session) {
1044
+ return {
1045
+ error: SessionNotFoundErrorCode,
1046
+ error_message: 'No session found for the provided authentication',
1047
+ error_is_fatal: true,
1048
+ };
1049
+ }
1050
+ const tools = [listSessionsTool];
1051
+ for (const tool of session.tools.values()) {
1052
+ const sessionAwareTool = {
1053
+ name: tool.name,
1054
+ description: tool.description,
1055
+ inputSchema: {
1056
+ type: 'object',
1057
+ properties: {
1058
+ session_id: {
1059
+ type: 'string',
1060
+ description: 'Session ID (optional - will auto-select if only one session active)',
1061
+ },
1062
+ ...(tool.inputSchema?.properties || {}),
1063
+ },
1064
+ required: tool.inputSchema?.required || [],
1065
+ },
1066
+ // Forward _meta (e.g., _meta.ui.resourceUri for MCP Apps)
1067
+ ...(tool._meta ? { _meta: tool._meta } : {}),
1068
+ };
1069
+ tools.push(sessionAwareTool);
1070
+ }
1071
+ return { tools };
1072
+ }, _MCPWebBridge_handleToolCall = async function _MCPWebBridge_handleToolCall(sessions, params) {
1073
+ const { name: toolName, arguments: toolInput, _meta } = params || {};
1074
+ if (!toolName) {
1075
+ return { error: ToolNameRequiredErrorCode };
1076
+ }
1077
+ const queryId = _meta?.queryId;
1078
+ if (queryId) {
1079
+ const query = __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").get(queryId);
1080
+ if (!query) {
1081
+ return { error: QueryNotFoundErrorCode };
1082
+ }
1083
+ if (query.state !== 'active') {
1084
+ return { error: QueryNotActiveErrorCode };
1085
+ }
1086
+ if (query.restrictTools && query.tools) {
1087
+ const allowed = query.tools.some((t) => t.name === toolName);
1088
+ if (!allowed) {
1089
+ return {
1090
+ error: ToolNotAllowedErrorCode,
1091
+ details: 'The query restricts the allowed tool calls. Use one of `allowed_tools`.',
1092
+ allowed_tools: query.tools.map((t) => t.name),
1093
+ };
1094
+ }
1095
+ }
1096
+ }
1097
+ if (toolName === 'list_sessions') {
1098
+ return { sessions: __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_listSessions).call(this, sessions) };
1099
+ }
1100
+ const [sessionId, session] = __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_getSessionAndSessionId).call(this, sessions, toolInput?.session_id || _meta?.sessionId) || [];
1101
+ if (!sessionId || !session) {
1102
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_createSessionNotFoundError).call(this, sessions);
1103
+ }
1104
+ if (!session.tools.has(toolName)) {
1105
+ return {
1106
+ error: ToolNotFoundErrorCode,
1107
+ available_tools: Array.from(session.tools.keys()),
1108
+ };
1109
+ }
1110
+ // Strip session_id from tool input before forwarding — it's a routing
1111
+ // parameter injected by the bridge, not an actual tool argument.
1112
+ const { session_id: _, ...forwardedInput } = toolInput || {};
1113
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_forwardToolCallToSession).call(this, sessionId, toolName, forwardedInput, queryId);
1114
+ }, _MCPWebBridge_handleResourcesList = async function _MCPWebBridge_handleResourcesList(sessions, params) {
1115
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_getSessionFromMetaParams).call(this, sessions, params);
1116
+ const sessionListResource = {
1117
+ uri: 'sessions://list',
1118
+ name: 'sessions',
1119
+ description: 'List of all active browser sessions for this authentication context',
1120
+ mimeType: 'application/json',
1121
+ };
1122
+ if (!session && sessions.size > 1) {
1123
+ return {
1124
+ resources: [sessionListResource],
1125
+ isError: true,
1126
+ error: SessionNotSpecifiedErrorCode,
1127
+ error_message: SessionNotSpecifiedErrorDetails,
1128
+ error_is_fatal: false,
1129
+ available_sessions: __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_listSessions).call(this, sessions),
1130
+ };
1131
+ }
1132
+ if (!session) {
1133
+ return {
1134
+ error: SessionNotFoundErrorCode,
1135
+ error_message: 'No session found for the provided authentication',
1136
+ error_is_fatal: true,
1137
+ };
1138
+ }
1139
+ const resources = [sessionListResource];
1140
+ // Add frontend-registered resources
1141
+ for (const resource of session.resources.values()) {
1142
+ resources.push({
1143
+ uri: resource.uri,
1144
+ name: resource.name,
1145
+ description: resource.description,
1146
+ mimeType: resource.mimeType ?? 'text/html',
1147
+ });
1148
+ }
1149
+ return { resources };
1150
+ }, _MCPWebBridge_handleResourceRead = async function _MCPWebBridge_handleResourceRead(sessions, params) {
1151
+ const { uri, _meta } = params || {};
1152
+ if (!uri) {
1153
+ return { error: 'Resource URI is required' };
1154
+ }
1155
+ if (uri === 'sessions://list') {
1156
+ const sessionData = __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_listSessions).call(this, sessions);
1157
+ return {
1158
+ contents: [
1159
+ {
1160
+ uri: 'sessions://list',
1161
+ mimeType: 'application/json',
1162
+ text: JSON.stringify(sessionData, null, 2),
1163
+ },
1164
+ ],
1165
+ };
1166
+ }
1167
+ // Look for frontend-registered resource
1168
+ const [sessionId, session] = __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_getSessionAndSessionId).call(this, sessions, _meta?.sessionId) || [];
1169
+ if (!sessionId || !session) {
1170
+ // If no session specified and multiple sessions, check all sessions for the resource
1171
+ for (const [sid, sess] of sessions.entries()) {
1172
+ if (sess.resources.has(uri)) {
1173
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_forwardResourceReadToSession).call(this, sid, uri);
1174
+ }
1175
+ }
1176
+ return { error: 'Resource not found' };
1177
+ }
1178
+ if (!session.resources.has(uri)) {
1179
+ return { error: 'Resource not found' };
1180
+ }
1181
+ return __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_forwardResourceReadToSession).call(this, sessionId, uri);
1182
+ }, _MCPWebBridge_listSessions = function _MCPWebBridge_listSessions(sessions) {
1183
+ return Array.from(sessions.entries()).map(([key, session]) => ({
1184
+ session_id: key,
1185
+ session_name: session.sessionName,
1186
+ origin: session.origin,
1187
+ page_title: session.pageTitle,
1188
+ connected_at: new Date(session.connectedAt).toISOString(),
1189
+ last_activity: new Date(session.lastActivity).toISOString(),
1190
+ available_tools: Array.from(session.tools.keys()),
1191
+ }));
1192
+ }, _MCPWebBridge_handlePromptsList = async function _MCPWebBridge_handlePromptsList(sessions, params) {
1193
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_getSessionFromMetaParams).call(this, sessions, params);
1194
+ if (!session && sessions.size > 1) {
1195
+ return {
1196
+ prompts: [],
1197
+ isError: true,
1198
+ error: SessionNotSpecifiedErrorCode,
1199
+ error_message: SessionNotSpecifiedErrorDetails,
1200
+ error_is_fatal: false,
1201
+ available_sessions: __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_listSessions).call(this, sessions),
1202
+ };
1203
+ }
1204
+ if (!session) {
1205
+ return {
1206
+ error: SessionNotFoundErrorCode,
1207
+ error_message: 'No session found for the provided authentication',
1208
+ error_is_fatal: true,
1209
+ };
1210
+ }
1211
+ return { prompts: [] };
1212
+ }, _MCPWebBridge_forwardToolCallToSession = async function _MCPWebBridge_forwardToolCallToSession(sessionId, toolName, toolInput, queryId) {
1213
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(sessionId);
1214
+ if (!session || session.ws.readyState !== 'OPEN') {
1215
+ return { error: 'Session not available' };
1216
+ }
1217
+ const requestId = crypto.randomUUID();
1218
+ const toolCall = {
1219
+ type: 'tool-call',
1220
+ requestId,
1221
+ toolName,
1222
+ toolInput,
1223
+ ...(queryId && { queryId }),
1224
+ };
1225
+ return new Promise((resolve) => {
1226
+ let timeoutId;
1227
+ const handleResponse = (data) => {
1228
+ try {
1229
+ const message = JSON.parse(data);
1230
+ if (message.type === 'tool-response' &&
1231
+ message.requestId === requestId) {
1232
+ if (timeoutId) {
1233
+ __classPrivateFieldGet(this, _MCPWebBridge_scheduler, "f").cancel(timeoutId);
1234
+ }
1235
+ __classPrivateFieldGet(this, _MCPWebBridge_toolResponseHandlers, "f").delete(requestId);
1236
+ session.ws.offMessage(handleResponse);
1237
+ const toolResult = message.result;
1238
+ if (queryId) {
1239
+ const query = __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").get(queryId);
1240
+ if (query) {
1241
+ query.toolCalls.push({
1242
+ tool: toolName,
1243
+ arguments: toolInput,
1244
+ result: toolResult,
1245
+ });
1246
+ if (query.responseTool === toolName) {
1247
+ if (!(toolResult &&
1248
+ typeof toolResult === 'object' &&
1249
+ 'error' in toolResult)) {
1250
+ const bridgeMessage = QueryCompleteBridgeMessageSchema.parse({
1251
+ uuid: queryId,
1252
+ message: undefined,
1253
+ toolCalls: query.toolCalls,
1254
+ });
1255
+ if (query.ws.readyState === 'OPEN') {
1256
+ query.ws.send(JSON.stringify(bridgeMessage));
1257
+ }
1258
+ __classPrivateFieldGet(this, _MCPWebBridge_queries, "f").delete(queryId);
1259
+ }
1260
+ }
1261
+ }
1262
+ }
1263
+ resolve(toolResult);
1264
+ }
1265
+ }
1266
+ catch {
1267
+ // Ignore invalid JSON
1268
+ }
1269
+ };
1270
+ // Set up timeout
1271
+ timeoutId = __classPrivateFieldGet(this, _MCPWebBridge_scheduler, "f").schedule(() => {
1272
+ __classPrivateFieldGet(this, _MCPWebBridge_toolResponseHandlers, "f").delete(requestId);
1273
+ session.ws.offMessage(handleResponse);
1274
+ resolve({ error: 'Tool call timeout' });
1275
+ }, 30000);
1276
+ __classPrivateFieldGet(this, _MCPWebBridge_toolResponseHandlers, "f").set(requestId, handleResponse);
1277
+ session.ws.onMessage(handleResponse);
1278
+ session.ws.send(JSON.stringify(toolCall));
1279
+ });
1280
+ }, _MCPWebBridge_forwardResourceReadToSession = async function _MCPWebBridge_forwardResourceReadToSession(sessionId, uri) {
1281
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(sessionId);
1282
+ if (!session || session.ws.readyState !== 'OPEN') {
1283
+ return { error: 'Session not available' };
1284
+ }
1285
+ const requestId = crypto.randomUUID();
1286
+ const resourceRead = {
1287
+ type: 'resource-read',
1288
+ requestId,
1289
+ uri,
1290
+ };
1291
+ return new Promise((resolve) => {
1292
+ let timeoutId;
1293
+ const handleResponse = (data) => {
1294
+ try {
1295
+ const message = JSON.parse(data);
1296
+ if (message.type === 'resource-response' &&
1297
+ message.requestId === requestId) {
1298
+ if (timeoutId) {
1299
+ __classPrivateFieldGet(this, _MCPWebBridge_scheduler, "f").cancel(timeoutId);
1300
+ }
1301
+ __classPrivateFieldGet(this, _MCPWebBridge_resourceResponseHandlers, "f").delete(requestId);
1302
+ session.ws.offMessage(handleResponse);
1303
+ if (message.error) {
1304
+ resolve({ error: message.error });
1305
+ return;
1306
+ }
1307
+ // Build MCP resource read response
1308
+ if (message.blob) {
1309
+ // Binary content (base64 encoded)
1310
+ resolve({
1311
+ contents: [
1312
+ {
1313
+ uri,
1314
+ mimeType: message.mimeType,
1315
+ blob: message.blob,
1316
+ },
1317
+ ],
1318
+ });
1319
+ }
1320
+ else {
1321
+ // Text content
1322
+ resolve({
1323
+ contents: [
1324
+ {
1325
+ uri,
1326
+ mimeType: message.mimeType,
1327
+ text: message.content,
1328
+ },
1329
+ ],
1330
+ });
1331
+ }
1332
+ }
1333
+ }
1334
+ catch {
1335
+ // Ignore invalid JSON
1336
+ }
1337
+ };
1338
+ // Set up timeout
1339
+ timeoutId = __classPrivateFieldGet(this, _MCPWebBridge_scheduler, "f").schedule(() => {
1340
+ __classPrivateFieldGet(this, _MCPWebBridge_resourceResponseHandlers, "f").delete(requestId);
1341
+ session.ws.offMessage(handleResponse);
1342
+ resolve({ error: 'Resource read timeout' });
1343
+ }, 30000);
1344
+ __classPrivateFieldGet(this, _MCPWebBridge_resourceResponseHandlers, "f").set(requestId, handleResponse);
1345
+ session.ws.onMessage(handleResponse);
1346
+ session.ws.send(JSON.stringify(resourceRead));
1347
+ });
1348
+ }, _MCPWebBridge_decrementQueryCount = function _MCPWebBridge_decrementQueryCount(authToken) {
1349
+ const count = __classPrivateFieldGet(this, _MCPWebBridge_tokenQueryCounts, "f").get(authToken) ?? 0;
1350
+ if (count <= 1) {
1351
+ __classPrivateFieldGet(this, _MCPWebBridge_tokenQueryCounts, "f").delete(authToken);
1352
+ }
1353
+ else {
1354
+ __classPrivateFieldGet(this, _MCPWebBridge_tokenQueryCounts, "f").set(authToken, count - 1);
1355
+ }
1356
+ }, _MCPWebBridge_decrementQueryCountForQuery = function _MCPWebBridge_decrementQueryCountForQuery(query) {
1357
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(query.sessionId);
1358
+ if (session) {
1359
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_decrementQueryCount).call(this, session.authToken);
1360
+ }
1361
+ }, _MCPWebBridge_closeOldestSessionForToken = function _MCPWebBridge_closeOldestSessionForToken(authToken) {
1362
+ const sessionIds = __classPrivateFieldGet(this, _MCPWebBridge_tokenSessionIds, "f").get(authToken);
1363
+ if (!sessionIds || sessionIds.size === 0)
1364
+ return;
1365
+ let oldest = null;
1366
+ for (const sessionId of sessionIds) {
1367
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(sessionId);
1368
+ if (session && (!oldest || session.connectedAt < oldest.connectedAt)) {
1369
+ oldest = { sessionId, connectedAt: session.connectedAt };
1370
+ }
1371
+ }
1372
+ if (oldest) {
1373
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(oldest.sessionId);
1374
+ if (session) {
1375
+ session.ws.send(JSON.stringify({
1376
+ type: 'session-closed',
1377
+ reason: 'Session limit exceeded, closing oldest session',
1378
+ code: SessionLimitExceededErrorCode,
1379
+ }));
1380
+ session.ws.close(1008, 'Session limit exceeded');
1381
+ }
1382
+ }
1383
+ }, _MCPWebBridge_cleanupSession = function _MCPWebBridge_cleanupSession(sessionId) {
1384
+ const session = __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").get(sessionId);
1385
+ if (session) {
1386
+ const sessionIds = __classPrivateFieldGet(this, _MCPWebBridge_tokenSessionIds, "f").get(session.authToken);
1387
+ if (sessionIds) {
1388
+ sessionIds.delete(sessionId);
1389
+ if (sessionIds.size === 0) {
1390
+ __classPrivateFieldGet(this, _MCPWebBridge_tokenSessionIds, "f").delete(session.authToken);
1391
+ }
1392
+ }
1393
+ // Notify connected MCP clients about tool changes (tools removed)
1394
+ __classPrivateFieldGet(this, _MCPWebBridge_instances, "m", _MCPWebBridge_notifyToolsChanged).call(this, session.authToken);
1395
+ }
1396
+ __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f").delete(sessionId);
1397
+ }, _MCPWebBridge_startSessionTimeoutChecker = function _MCPWebBridge_startSessionTimeoutChecker() {
1398
+ const maxDuration = __classPrivateFieldGet(this, _MCPWebBridge_config, "f").sessionMaxDurationMs;
1399
+ if (!maxDuration)
1400
+ return;
1401
+ __classPrivateFieldSet(this, _MCPWebBridge_sessionTimeoutIntervalId, __classPrivateFieldGet(this, _MCPWebBridge_scheduler, "f").scheduleInterval(() => {
1402
+ const now = Date.now();
1403
+ for (const [_sessionId, session] of __classPrivateFieldGet(this, _MCPWebBridge_sessions, "f")) {
1404
+ if (now - session.connectedAt > maxDuration) {
1405
+ session.ws.send(JSON.stringify({
1406
+ type: 'session-expired',
1407
+ code: SessionExpiredErrorCode,
1408
+ }));
1409
+ session.ws.close(1008, 'Session expired');
1410
+ }
1411
+ }
1412
+ }, 60000), "f");
1413
+ }, _MCPWebBridge_startMcpSessionTimeoutChecker = function _MCPWebBridge_startMcpSessionTimeoutChecker() {
1414
+ // Check every minute
1415
+ __classPrivateFieldSet(this, _MCPWebBridge_mcpSessionTimeoutIntervalId, __classPrivateFieldGet(this, _MCPWebBridge_scheduler, "f").scheduleInterval(() => {
1416
+ const now = Date.now();
1417
+ for (const [sessionId, mcpSession] of __classPrivateFieldGet(this, _MCPWebBridge_mcpSessions, "f")) {
1418
+ const idleTime = now - mcpSession.lastActivity;
1419
+ if (idleTime > _a.MCP_SESSION_IDLE_TIMEOUT_MS) {
1420
+ // Clean up SSE stream if open
1421
+ if (mcpSession.sseCleanup) {
1422
+ mcpSession.sseCleanup();
1423
+ }
1424
+ __classPrivateFieldGet(this, _MCPWebBridge_mcpSessions, "f").delete(sessionId);
1425
+ console.log(`MCP session ${sessionId} expired after ${idleTime}ms of inactivity`);
1426
+ }
1427
+ }
1428
+ }, 60000), "f");
1429
+ }, _MCPWebBridge_notifyToolsChanged = function _MCPWebBridge_notifyToolsChanged(authToken) {
1430
+ for (const mcpSession of __classPrivateFieldGet(this, _MCPWebBridge_mcpSessions, "f").values()) {
1431
+ // Only notify sessions with matching auth token
1432
+ if (mcpSession.authToken === authToken && mcpSession.sseWriter) {
1433
+ const notification = {
1434
+ jsonrpc: '2.0',
1435
+ method: 'notifications/tools/list_changed',
1436
+ };
1437
+ mcpSession.sseWriter(JSON.stringify(notification));
1438
+ }
1439
+ }
1440
+ }, _MCPWebBridge_handleSSEStream = function _MCPWebBridge_handleSSEStream(req) {
1441
+ const mcpSessionId = req.headers.get('mcp-session-id');
1442
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").debug) {
1443
+ console.log(`[MCP Debug] SSE stream request, session: ${mcpSessionId || '[NONE]'}`);
1444
+ }
1445
+ if (!mcpSessionId) {
1446
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").debug) {
1447
+ console.log(`[MCP Debug] SSE Error: Missing Mcp-Session-Id header`);
1448
+ }
1449
+ // Return a regular response indicating error - we need Mcp-Session-Id
1450
+ return sseResponse((writer, _onClose) => {
1451
+ writer(JSON.stringify({
1452
+ jsonrpc: '2.0',
1453
+ error: {
1454
+ code: -32600,
1455
+ message: 'Mcp-Session-Id header required for SSE stream',
1456
+ },
1457
+ }));
1458
+ });
1459
+ }
1460
+ const mcpSession = __classPrivateFieldGet(this, _MCPWebBridge_mcpSessions, "f").get(mcpSessionId);
1461
+ if (!mcpSession) {
1462
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").debug) {
1463
+ console.log(`[MCP Debug] SSE Error: MCP session not found`);
1464
+ }
1465
+ return sseResponse((writer, _onClose) => {
1466
+ writer(JSON.stringify({
1467
+ jsonrpc: '2.0',
1468
+ error: {
1469
+ code: -32600,
1470
+ message: 'MCP session not found',
1471
+ },
1472
+ }));
1473
+ });
1474
+ }
1475
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").debug) {
1476
+ console.log(`[MCP Debug] SSE stream opened successfully`);
1477
+ }
1478
+ return sseResponse((writer, onClose) => {
1479
+ // Store the writer so we can push notifications later
1480
+ mcpSession.sseWriter = writer;
1481
+ mcpSession.lastActivity = Date.now();
1482
+ // Set up cleanup for when client disconnects
1483
+ const cleanup = () => {
1484
+ if (__classPrivateFieldGet(this, _MCPWebBridge_config, "f").debug) {
1485
+ console.log(`[MCP Debug] SSE stream closed for session ${mcpSessionId}`);
1486
+ }
1487
+ mcpSession.sseWriter = undefined;
1488
+ mcpSession.sseCleanup = undefined;
1489
+ };
1490
+ mcpSession.sseCleanup = cleanup;
1491
+ // Register the onClose callback
1492
+ // Note: The adapter will call onClose when the client disconnects
1493
+ // We store our cleanup function so we can also call it manually
1494
+ });
1495
+ }, _MCPWebBridge_handleMcpSessionDelete = function _MCPWebBridge_handleMcpSessionDelete(sessionId) {
1496
+ const mcpSession = __classPrivateFieldGet(this, _MCPWebBridge_mcpSessions, "f").get(sessionId);
1497
+ if (!mcpSession) {
1498
+ return jsonResponse(404, { error: 'MCP session not found' });
1499
+ }
1500
+ // Clean up SSE stream if open
1501
+ if (mcpSession.sseCleanup) {
1502
+ mcpSession.sseCleanup();
1503
+ }
1504
+ __classPrivateFieldGet(this, _MCPWebBridge_mcpSessions, "f").delete(sessionId);
1505
+ return jsonResponse(200, { success: true });
1506
+ };
1507
+ MCPWebBridge.MCP_SESSION_IDLE_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour
1508
+ //# sourceMappingURL=core.js.map