@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
@@ -0,0 +1,443 @@
1
+ import { test, expect, afterEach, describe } from 'bun:test';
2
+ import { MCPWebBridgeNode } from './adapters/node.js';
3
+ import { WebSocket } from 'ws';
4
+
5
+ // Helper to create a mock WebSocket client
6
+ function createMockClient(port: number, sessionId: string): Promise<WebSocket> {
7
+ return new Promise((resolve, reject) => {
8
+ const ws = new WebSocket(`ws://localhost:${port}?session=${sessionId}`);
9
+ ws.on('open', () => resolve(ws));
10
+ ws.on('error', reject);
11
+ });
12
+ }
13
+
14
+ // Helper to wait for a specific message type
15
+ function waitForMessage<T>(ws: WebSocket, type: string, timeout = 5000): Promise<T> {
16
+ return new Promise((resolve, reject) => {
17
+ const timer = setTimeout(() => reject(new Error(`Timeout waiting for ${type}`)), timeout);
18
+
19
+ const handler = (data: Buffer) => {
20
+ try {
21
+ const message = JSON.parse(data.toString());
22
+ if (message.type === type) {
23
+ clearTimeout(timer);
24
+ ws.removeListener('message', handler);
25
+ resolve(message);
26
+ }
27
+ } catch {
28
+ // Ignore parse errors
29
+ }
30
+ };
31
+
32
+ ws.on('message', handler);
33
+ });
34
+ }
35
+
36
+ // Helper to authenticate a client with optional session name
37
+ async function authenticateClient(
38
+ ws: WebSocket,
39
+ authToken: string,
40
+ sessionName?: string
41
+ ): Promise<void> {
42
+ ws.send(JSON.stringify({
43
+ type: 'authenticate',
44
+ authToken,
45
+ origin: 'http://localhost:3000',
46
+ pageTitle: 'Test Page',
47
+ sessionName,
48
+ userAgent: 'Test Agent',
49
+ timestamp: Date.now(),
50
+ }));
51
+
52
+ await waitForMessage(ws, 'authenticated');
53
+ }
54
+
55
+ // Helper to register a tool
56
+ function registerTool(ws: WebSocket, name: string, description: string): void {
57
+ ws.send(JSON.stringify({
58
+ type: 'register-tool',
59
+ tool: { name, description },
60
+ }));
61
+ }
62
+
63
+ // Helper to make MCP HTTP requests
64
+ async function mcpRequest(
65
+ port: number,
66
+ method: string,
67
+ params?: Record<string, unknown>,
68
+ authToken?: string
69
+ ): Promise<unknown> {
70
+ const response = await fetch(`http://localhost:${port}`, {
71
+ method: 'POST',
72
+ headers: {
73
+ 'Content-Type': 'application/json',
74
+ ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
75
+ },
76
+ body: JSON.stringify({
77
+ jsonrpc: '2.0',
78
+ id: 1,
79
+ method,
80
+ params,
81
+ }),
82
+ });
83
+ return response.json();
84
+ }
85
+
86
+ // ============================================================================
87
+ // Session Naming Tests
88
+ // ============================================================================
89
+
90
+ describe('Session Naming', () => {
91
+ let bridge: MCPWebBridgeNode;
92
+ let clients: WebSocket[] = [];
93
+ const port = 4701;
94
+
95
+ afterEach(async () => {
96
+ for (const client of clients) {
97
+ if (client.readyState === WebSocket.OPEN) {
98
+ client.close();
99
+ }
100
+ }
101
+ clients = [];
102
+
103
+ if (bridge) {
104
+ await bridge.close();
105
+ }
106
+ });
107
+
108
+ test('session with a name authenticates successfully', async () => {
109
+ bridge = new MCPWebBridgeNode({
110
+ name: 'Test Bridge',
111
+ description: 'Test',
112
+ port,
113
+ });
114
+
115
+ const client = await createMockClient(port, 'session-named-1');
116
+ clients.push(client);
117
+ await authenticateClient(client, 'token-1', 'Game 1');
118
+
119
+ expect(client.readyState).toBe(WebSocket.OPEN);
120
+ });
121
+
122
+ test('sessions without names work as before', async () => {
123
+ bridge = new MCPWebBridgeNode({
124
+ name: 'Test Bridge',
125
+ description: 'Test',
126
+ port,
127
+ });
128
+
129
+ const client1 = await createMockClient(port, 'session-unnamed-1');
130
+ clients.push(client1);
131
+ await authenticateClient(client1, 'token-1');
132
+
133
+ const client2 = await createMockClient(port, 'session-unnamed-2');
134
+ clients.push(client2);
135
+ await authenticateClient(client2, 'token-1');
136
+
137
+ expect(client1.readyState).toBe(WebSocket.OPEN);
138
+ expect(client2.readyState).toBe(WebSocket.OPEN);
139
+ });
140
+
141
+ test('rejects duplicate session name under same auth token', async () => {
142
+ bridge = new MCPWebBridgeNode({
143
+ name: 'Test Bridge',
144
+ description: 'Test',
145
+ port,
146
+ });
147
+
148
+ const authToken = 'token-dup-name';
149
+
150
+ // First session with name "Game 1"
151
+ const client1 = await createMockClient(port, 'session-dup-1');
152
+ clients.push(client1);
153
+ await authenticateClient(client1, authToken, 'Game 1');
154
+
155
+ // Second session with same name "Game 1" — should be rejected
156
+ const client2 = await createMockClient(port, 'session-dup-2');
157
+ clients.push(client2);
158
+
159
+ const closePromise = new Promise<{ code: number; reason: string }>((resolve) => {
160
+ client2.on('close', (code, reason) => {
161
+ resolve({ code, reason: reason.toString() });
162
+ });
163
+ });
164
+
165
+ client2.send(JSON.stringify({
166
+ type: 'authenticate',
167
+ authToken,
168
+ origin: 'http://localhost:3000',
169
+ sessionName: 'Game 1',
170
+ timestamp: Date.now(),
171
+ }));
172
+
173
+ // Should receive authentication-failed message
174
+ const failedMessage = await waitForMessage<{ type: string; error: string; code: string }>(
175
+ client2,
176
+ 'authentication-failed'
177
+ );
178
+ expect(failedMessage.code).toBe('SessionNameAlreadyInUse');
179
+ expect(failedMessage.error).toContain('Game 1');
180
+
181
+ // Connection should be closed
182
+ const closeResult = await closePromise;
183
+ expect(closeResult.code).toBe(1008);
184
+ expect(closeResult.reason).toBe('Session name already in use');
185
+ });
186
+
187
+ test('allows same session name under different auth tokens', async () => {
188
+ bridge = new MCPWebBridgeNode({
189
+ name: 'Test Bridge',
190
+ description: 'Test',
191
+ port,
192
+ });
193
+
194
+ // "Game 1" under token-a
195
+ const client1 = await createMockClient(port, 'session-cross-1');
196
+ clients.push(client1);
197
+ await authenticateClient(client1, 'token-a', 'Game 1');
198
+
199
+ // "Game 1" under token-b — should succeed (different token)
200
+ const client2 = await createMockClient(port, 'session-cross-2');
201
+ clients.push(client2);
202
+ await authenticateClient(client2, 'token-b', 'Game 1');
203
+
204
+ expect(client1.readyState).toBe(WebSocket.OPEN);
205
+ expect(client2.readyState).toBe(WebSocket.OPEN);
206
+ });
207
+
208
+ test('allows different session names under same auth token', async () => {
209
+ bridge = new MCPWebBridgeNode({
210
+ name: 'Test Bridge',
211
+ description: 'Test',
212
+ port,
213
+ });
214
+
215
+ const authToken = 'token-diff-names';
216
+
217
+ const client1 = await createMockClient(port, 'session-diff-1');
218
+ clients.push(client1);
219
+ await authenticateClient(client1, authToken, 'Game 1');
220
+
221
+ const client2 = await createMockClient(port, 'session-diff-2');
222
+ clients.push(client2);
223
+ await authenticateClient(client2, authToken, 'Game 2');
224
+
225
+ expect(client1.readyState).toBe(WebSocket.OPEN);
226
+ expect(client2.readyState).toBe(WebSocket.OPEN);
227
+ });
228
+
229
+ test('allows named and unnamed sessions under same token', async () => {
230
+ bridge = new MCPWebBridgeNode({
231
+ name: 'Test Bridge',
232
+ description: 'Test',
233
+ port,
234
+ });
235
+
236
+ const authToken = 'token-mixed';
237
+
238
+ const client1 = await createMockClient(port, 'session-mixed-1');
239
+ clients.push(client1);
240
+ await authenticateClient(client1, authToken, 'Game 1');
241
+
242
+ // Unnamed session — should work fine
243
+ const client2 = await createMockClient(port, 'session-mixed-2');
244
+ clients.push(client2);
245
+ await authenticateClient(client2, authToken);
246
+
247
+ expect(client1.readyState).toBe(WebSocket.OPEN);
248
+ expect(client2.readyState).toBe(WebSocket.OPEN);
249
+ });
250
+
251
+ test('reclaiming a name after the original session disconnects', async () => {
252
+ bridge = new MCPWebBridgeNode({
253
+ name: 'Test Bridge',
254
+ description: 'Test',
255
+ port,
256
+ });
257
+
258
+ const authToken = 'token-reclaim';
259
+
260
+ // Create and authenticate with "Game 1"
261
+ const client1 = await createMockClient(port, 'session-reclaim-1');
262
+ clients.push(client1);
263
+ await authenticateClient(client1, authToken, 'Game 1');
264
+
265
+ // Disconnect client1
266
+ client1.close();
267
+ await new Promise(resolve => setTimeout(resolve, 100));
268
+
269
+ // New client should be able to use "Game 1"
270
+ const client2 = await createMockClient(port, 'session-reclaim-2');
271
+ clients.push(client2);
272
+ await authenticateClient(client2, authToken, 'Game 1');
273
+
274
+ expect(client2.readyState).toBe(WebSocket.OPEN);
275
+ });
276
+ });
277
+
278
+ // ============================================================================
279
+ // Session Name in list_sessions
280
+ // ============================================================================
281
+
282
+ describe('Session Name in list_sessions', () => {
283
+ let bridge: MCPWebBridgeNode;
284
+ let clients: WebSocket[] = [];
285
+ const port = 4702;
286
+
287
+ afterEach(async () => {
288
+ for (const client of clients) {
289
+ if (client.readyState === WebSocket.OPEN) {
290
+ client.close();
291
+ }
292
+ }
293
+ clients = [];
294
+
295
+ if (bridge) {
296
+ await bridge.close();
297
+ }
298
+ });
299
+
300
+ test('list_sessions includes session_name for named sessions', async () => {
301
+ bridge = new MCPWebBridgeNode({
302
+ name: 'Test Bridge',
303
+ description: 'Test',
304
+ port,
305
+ });
306
+
307
+ const authToken = 'token-list';
308
+
309
+ const client1 = await createMockClient(port, 'session-list-1');
310
+ clients.push(client1);
311
+ await authenticateClient(client1, authToken, 'Game 1');
312
+ registerTool(client1, 'move_piece', 'Move a piece');
313
+
314
+ // Small delay for tool registration
315
+ await new Promise(resolve => setTimeout(resolve, 100));
316
+
317
+ const client2 = await createMockClient(port, 'session-list-2');
318
+ clients.push(client2);
319
+ await authenticateClient(client2, authToken, 'Game 2');
320
+ registerTool(client2, 'move_piece', 'Move a piece');
321
+
322
+ // Small delay for tool registration
323
+ await new Promise(resolve => setTimeout(resolve, 100));
324
+
325
+ // Initialize MCP session first
326
+ const initResult = await fetch(`http://localhost:${port}`, {
327
+ method: 'POST',
328
+ headers: {
329
+ 'Content-Type': 'application/json',
330
+ Authorization: `Bearer ${authToken}`,
331
+ },
332
+ body: JSON.stringify({
333
+ jsonrpc: '2.0',
334
+ id: 0,
335
+ method: 'initialize',
336
+ params: {},
337
+ }),
338
+ });
339
+ const initJson = await initResult.json() as { result: unknown };
340
+ const mcpSessionId = initResult.headers.get('mcp-session-id');
341
+
342
+ // Call list_sessions tool
343
+ const response = await fetch(`http://localhost:${port}`, {
344
+ method: 'POST',
345
+ headers: {
346
+ 'Content-Type': 'application/json',
347
+ Authorization: `Bearer ${authToken}`,
348
+ ...(mcpSessionId ? { 'Mcp-Session-Id': mcpSessionId } : {}),
349
+ },
350
+ body: JSON.stringify({
351
+ jsonrpc: '2.0',
352
+ id: 1,
353
+ method: 'tools/call',
354
+ params: {
355
+ name: 'list_sessions',
356
+ arguments: {},
357
+ },
358
+ }),
359
+ });
360
+
361
+ const result = await response.json() as {
362
+ result: {
363
+ content: Array<{ type: string; text: string }>;
364
+ };
365
+ };
366
+
367
+ const sessions = JSON.parse(result.result.content[0].text).sessions;
368
+ expect(sessions).toHaveLength(2);
369
+
370
+ const session1 = sessions.find((s: { session_name: string }) => s.session_name === 'Game 1');
371
+ const session2 = sessions.find((s: { session_name: string }) => s.session_name === 'Game 2');
372
+
373
+ expect(session1).toBeDefined();
374
+ expect(session1.session_id).toBe('session-list-1');
375
+ expect(session1.session_name).toBe('Game 1');
376
+
377
+ expect(session2).toBeDefined();
378
+ expect(session2.session_id).toBe('session-list-2');
379
+ expect(session2.session_name).toBe('Game 2');
380
+ });
381
+
382
+ test('list_sessions returns undefined session_name for unnamed sessions', async () => {
383
+ bridge = new MCPWebBridgeNode({
384
+ name: 'Test Bridge',
385
+ description: 'Test',
386
+ port,
387
+ });
388
+
389
+ const authToken = 'token-list-unnamed';
390
+
391
+ const client1 = await createMockClient(port, 'session-unnamed-list');
392
+ clients.push(client1);
393
+ await authenticateClient(client1, authToken);
394
+ registerTool(client1, 'some_tool', 'Some tool');
395
+
396
+ await new Promise(resolve => setTimeout(resolve, 100));
397
+
398
+ // Initialize MCP session
399
+ const initResult = await fetch(`http://localhost:${port}`, {
400
+ method: 'POST',
401
+ headers: {
402
+ 'Content-Type': 'application/json',
403
+ Authorization: `Bearer ${authToken}`,
404
+ },
405
+ body: JSON.stringify({
406
+ jsonrpc: '2.0',
407
+ id: 0,
408
+ method: 'initialize',
409
+ params: {},
410
+ }),
411
+ });
412
+ const mcpSessionId = initResult.headers.get('mcp-session-id');
413
+
414
+ // Call list_sessions
415
+ const response = await fetch(`http://localhost:${port}`, {
416
+ method: 'POST',
417
+ headers: {
418
+ 'Content-Type': 'application/json',
419
+ Authorization: `Bearer ${authToken}`,
420
+ ...(mcpSessionId ? { 'Mcp-Session-Id': mcpSessionId } : {}),
421
+ },
422
+ body: JSON.stringify({
423
+ jsonrpc: '2.0',
424
+ id: 1,
425
+ method: 'tools/call',
426
+ params: {
427
+ name: 'list_sessions',
428
+ arguments: {},
429
+ },
430
+ }),
431
+ });
432
+
433
+ const result = await response.json() as {
434
+ result: {
435
+ content: Array<{ type: string; text: string }>;
436
+ };
437
+ };
438
+
439
+ const sessions = JSON.parse(result.result.content[0].text).sessions;
440
+ expect(sessions).toHaveLength(1);
441
+ expect(sessions[0].session_name).toBeUndefined();
442
+ });
443
+ });
package/src/types.ts ADDED
@@ -0,0 +1,180 @@
1
+ import type {
2
+ McpRequestMetaParams,
3
+ QueryAcceptedMessage,
4
+ QueryCancelMessage,
5
+ QueryCompleteBridgeMessage,
6
+ QueryCompleteClientMessage,
7
+ QueryFailureMessage,
8
+ QueryMessage,
9
+ QueryProgressMessage,
10
+ ResourceMetadata,
11
+ ToolMetadata
12
+ } from '@mcp-web/types';
13
+ import type * as WS from 'ws';
14
+ import type { z } from 'zod';
15
+
16
+ export interface AuthenticateMessage {
17
+ type: 'authenticate';
18
+ sessionId: string;
19
+ authToken: string;
20
+ origin: string;
21
+ pageTitle?: string;
22
+ sessionName?: string;
23
+ userAgent?: string;
24
+ timestamp: number;
25
+ }
26
+
27
+ export interface AuthenticatedMessage {
28
+ type: 'authenticated';
29
+ mcpPort?: number;
30
+ sessionId: string;
31
+ success: boolean;
32
+ }
33
+
34
+ export interface AuthenticationFailedMessage {
35
+ type: 'authentication-failed';
36
+ error: string;
37
+ code: string;
38
+ }
39
+
40
+ export interface RegisterToolMessage {
41
+ type: 'register-tool';
42
+ tool: {
43
+ name: string;
44
+ description: string;
45
+ inputSchema?: z.core.JSONSchema.JSONSchema;
46
+ outputSchema?: z.core.JSONSchema.JSONSchema;
47
+ _meta?: Record<string, unknown>;
48
+ };
49
+ }
50
+
51
+ export interface RegisterResourceMessage {
52
+ type: 'register-resource';
53
+ resource: ResourceMetadata;
54
+ }
55
+
56
+ export interface ResourceReadMessage {
57
+ type: 'resource-read';
58
+ requestId: string;
59
+ uri: string;
60
+ }
61
+
62
+ export interface ResourceResponseMessage {
63
+ type: 'resource-response';
64
+ requestId: string;
65
+ content?: string;
66
+ blob?: string;
67
+ mimeType: string;
68
+ error?: string;
69
+ }
70
+
71
+ export interface ActivityMessage {
72
+ type: 'activity';
73
+ timestamp: number;
74
+ }
75
+
76
+ export interface ToolCallMessage {
77
+ type: 'tool-call';
78
+ requestId: string;
79
+ toolName: string;
80
+ toolInput?: Record<string, unknown>;
81
+ queryId?: string; // Added for query context
82
+ }
83
+
84
+ export interface ToolRegistrationErrorMessage {
85
+ type: 'tool-registration-error';
86
+ toolName: string;
87
+ error: string;
88
+ message: string;
89
+ }
90
+
91
+ export interface ToolResponseMessage {
92
+ type: 'tool-response';
93
+ requestId: string;
94
+ result: unknown;
95
+ }
96
+
97
+ export type FrontendMessage =
98
+ | AuthenticateMessage
99
+ | RegisterToolMessage
100
+ | RegisterResourceMessage
101
+ | ActivityMessage
102
+ | ToolResponseMessage
103
+ | ResourceResponseMessage
104
+ | QueryMessage
105
+ | QueryCompleteClientMessage
106
+ | QueryProgressMessage
107
+ | QueryCancelMessage;
108
+
109
+ export type BridgeMessage =
110
+ | AuthenticatedMessage
111
+ | AuthenticationFailedMessage
112
+ | ToolCallMessage
113
+ | ToolRegistrationErrorMessage
114
+ | ResourceReadMessage
115
+ | QueryAcceptedMessage
116
+ | QueryProgressMessage
117
+ | QueryCompleteBridgeMessage
118
+ | QueryFailureMessage
119
+ | QueryCancelMessage;
120
+
121
+ export interface TrackedToolCall {
122
+ tool: string;
123
+ arguments: unknown;
124
+ result: unknown;
125
+ }
126
+
127
+ export type QueryState = 'active' | 'completed' | 'failed' | 'cancelled';
128
+
129
+ export interface QueryTracking {
130
+ sessionId: string;
131
+ responseTool?: string;
132
+ toolCalls: TrackedToolCall[];
133
+ ws: WS.WebSocket;
134
+ state: QueryState;
135
+ tools?: ToolMetadata[];
136
+ restrictTools?: boolean;
137
+ }
138
+
139
+ export interface McpRequest {
140
+ jsonrpc: string;
141
+ id: string | number;
142
+ method: string;
143
+ params?: {
144
+ name?: string;
145
+ arguments?: Record<string, unknown>;
146
+ _meta?: McpRequestMetaParams;
147
+ };
148
+ }
149
+
150
+ export interface McpResponse {
151
+ jsonrpc: string;
152
+ id: string | number;
153
+ result?: unknown;
154
+ error?: {
155
+ code: number;
156
+ message: string;
157
+ data?: unknown;
158
+ };
159
+ }
160
+
161
+ export interface ToolDefinition {
162
+ name: string;
163
+ description: string;
164
+ inputSchema?: z.core.JSONSchema.JSONSchema;
165
+ outputSchema?: z.core.JSONSchema.JSONSchema;
166
+ handler?: string; // Reference to frontend handler
167
+ }
168
+
169
+ export interface SessionData {
170
+ ws: WS.WebSocket;
171
+ authToken: string;
172
+ origin: string;
173
+ pageTitle?: string;
174
+ sessionName?: string;
175
+ userAgent?: string;
176
+ connectedAt: number;
177
+ lastActivity: number;
178
+ tools: Map<string, ToolDefinition>;
179
+ resources: Map<string, ResourceMetadata>;
180
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "module": "node16",
5
+ "moduleResolution": "node16",
6
+ "lib": ["ES2022"],
7
+ "outDir": "dist",
8
+ "rootDir": "src"
9
+ },
10
+ "include": ["src/**/*"],
11
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
12
+ }