@mcp-ts/sdk 1.0.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 (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +297 -0
  3. package/dist/adapters/agui-adapter.d.mts +119 -0
  4. package/dist/adapters/agui-adapter.d.ts +119 -0
  5. package/dist/adapters/agui-adapter.js +109 -0
  6. package/dist/adapters/agui-adapter.js.map +1 -0
  7. package/dist/adapters/agui-adapter.mjs +107 -0
  8. package/dist/adapters/agui-adapter.mjs.map +1 -0
  9. package/dist/adapters/agui-middleware.d.mts +171 -0
  10. package/dist/adapters/agui-middleware.d.ts +171 -0
  11. package/dist/adapters/agui-middleware.js +429 -0
  12. package/dist/adapters/agui-middleware.js.map +1 -0
  13. package/dist/adapters/agui-middleware.mjs +417 -0
  14. package/dist/adapters/agui-middleware.mjs.map +1 -0
  15. package/dist/adapters/ai-adapter.d.mts +38 -0
  16. package/dist/adapters/ai-adapter.d.ts +38 -0
  17. package/dist/adapters/ai-adapter.js +82 -0
  18. package/dist/adapters/ai-adapter.js.map +1 -0
  19. package/dist/adapters/ai-adapter.mjs +80 -0
  20. package/dist/adapters/ai-adapter.mjs.map +1 -0
  21. package/dist/adapters/langchain-adapter.d.mts +46 -0
  22. package/dist/adapters/langchain-adapter.d.ts +46 -0
  23. package/dist/adapters/langchain-adapter.js +102 -0
  24. package/dist/adapters/langchain-adapter.js.map +1 -0
  25. package/dist/adapters/langchain-adapter.mjs +100 -0
  26. package/dist/adapters/langchain-adapter.mjs.map +1 -0
  27. package/dist/adapters/mastra-adapter.d.mts +49 -0
  28. package/dist/adapters/mastra-adapter.d.ts +49 -0
  29. package/dist/adapters/mastra-adapter.js +95 -0
  30. package/dist/adapters/mastra-adapter.js.map +1 -0
  31. package/dist/adapters/mastra-adapter.mjs +93 -0
  32. package/dist/adapters/mastra-adapter.mjs.map +1 -0
  33. package/dist/client/index.d.mts +119 -0
  34. package/dist/client/index.d.ts +119 -0
  35. package/dist/client/index.js +225 -0
  36. package/dist/client/index.js.map +1 -0
  37. package/dist/client/index.mjs +223 -0
  38. package/dist/client/index.mjs.map +1 -0
  39. package/dist/client/react.d.mts +151 -0
  40. package/dist/client/react.d.ts +151 -0
  41. package/dist/client/react.js +492 -0
  42. package/dist/client/react.js.map +1 -0
  43. package/dist/client/react.mjs +489 -0
  44. package/dist/client/react.mjs.map +1 -0
  45. package/dist/client/vue.d.mts +157 -0
  46. package/dist/client/vue.d.ts +157 -0
  47. package/dist/client/vue.js +474 -0
  48. package/dist/client/vue.js.map +1 -0
  49. package/dist/client/vue.mjs +471 -0
  50. package/dist/client/vue.mjs.map +1 -0
  51. package/dist/events-BP6WyRNh.d.mts +110 -0
  52. package/dist/events-BP6WyRNh.d.ts +110 -0
  53. package/dist/index.d.mts +10 -0
  54. package/dist/index.d.ts +10 -0
  55. package/dist/index.js +2784 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/index.mjs +2723 -0
  58. package/dist/index.mjs.map +1 -0
  59. package/dist/multi-session-client-BOFgPypS.d.ts +389 -0
  60. package/dist/multi-session-client-DMF3ED2O.d.mts +389 -0
  61. package/dist/server/index.d.mts +269 -0
  62. package/dist/server/index.d.ts +269 -0
  63. package/dist/server/index.js +2444 -0
  64. package/dist/server/index.js.map +1 -0
  65. package/dist/server/index.mjs +2414 -0
  66. package/dist/server/index.mjs.map +1 -0
  67. package/dist/shared/index.d.mts +24 -0
  68. package/dist/shared/index.d.ts +24 -0
  69. package/dist/shared/index.js +223 -0
  70. package/dist/shared/index.js.map +1 -0
  71. package/dist/shared/index.mjs +190 -0
  72. package/dist/shared/index.mjs.map +1 -0
  73. package/dist/types-SbDlA2VX.d.mts +153 -0
  74. package/dist/types-SbDlA2VX.d.ts +153 -0
  75. package/dist/utils-0qmYrqoa.d.mts +92 -0
  76. package/dist/utils-0qmYrqoa.d.ts +92 -0
  77. package/package.json +165 -0
  78. package/src/adapters/agui-adapter.ts +210 -0
  79. package/src/adapters/agui-middleware.ts +512 -0
  80. package/src/adapters/ai-adapter.ts +115 -0
  81. package/src/adapters/langchain-adapter.ts +127 -0
  82. package/src/adapters/mastra-adapter.ts +126 -0
  83. package/src/client/core/sse-client.ts +340 -0
  84. package/src/client/index.ts +26 -0
  85. package/src/client/react/index.ts +10 -0
  86. package/src/client/react/useMcp.ts +558 -0
  87. package/src/client/vue/index.ts +10 -0
  88. package/src/client/vue/useMcp.ts +542 -0
  89. package/src/index.ts +11 -0
  90. package/src/server/handlers/nextjs-handler.ts +216 -0
  91. package/src/server/handlers/sse-handler.ts +699 -0
  92. package/src/server/index.ts +57 -0
  93. package/src/server/mcp/multi-session-client.ts +132 -0
  94. package/src/server/mcp/oauth-client.ts +1168 -0
  95. package/src/server/mcp/storage-oauth-provider.ts +239 -0
  96. package/src/server/storage/file-backend.ts +169 -0
  97. package/src/server/storage/index.ts +115 -0
  98. package/src/server/storage/memory-backend.ts +132 -0
  99. package/src/server/storage/redis-backend.ts +210 -0
  100. package/src/server/storage/redis.ts +160 -0
  101. package/src/server/storage/types.ts +109 -0
  102. package/src/shared/constants.ts +29 -0
  103. package/src/shared/errors.ts +133 -0
  104. package/src/shared/events.ts +166 -0
  105. package/src/shared/index.ts +70 -0
  106. package/src/shared/types.ts +274 -0
  107. package/src/shared/utils.ts +16 -0
@@ -0,0 +1,558 @@
1
+ /**
2
+ * useMcp React Hook
3
+ * Manages MCP connections with SSE-based real-time updates
4
+ * Based on Cloudflare's agents pattern
5
+ */
6
+
7
+ import { useEffect, useRef, useState, useCallback } from 'react';
8
+ import { SSEClient, type SSEClientOptions } from '../core/sse-client';
9
+ import type { McpConnectionEvent, McpConnectionState } from '../../shared/events';
10
+ import type {
11
+ ToolInfo,
12
+ FinishAuthResult,
13
+ ListToolsRpcResult,
14
+ ListPromptsResult,
15
+ ListResourcesResult,
16
+ SessionInfo,
17
+ } from '../../shared/types';
18
+
19
+ export interface UseMcpOptions {
20
+ /**
21
+ * SSE endpoint URL
22
+ */
23
+ url: string;
24
+
25
+ /**
26
+ * User/Client identifier
27
+ */
28
+ identity: string;
29
+
30
+ /**
31
+ * Optional auth token
32
+ */
33
+ authToken?: string;
34
+
35
+ /**
36
+ * Auto-connect on mount
37
+ * @default true
38
+ */
39
+ autoConnect?: boolean;
40
+
41
+ /**
42
+ * Auto-initialize sessions on mount
43
+ * @default true
44
+ */
45
+ autoInitialize?: boolean;
46
+
47
+ /**
48
+ * Connection event callback
49
+ */
50
+ onConnectionEvent?: (event: McpConnectionEvent) => void;
51
+
52
+ /**
53
+ * Debug logging callback
54
+ */
55
+ onLog?: (level: string, message: string, metadata?: Record<string, unknown>) => void;
56
+ /**
57
+ * Optional callback to handle OAuth redirects (e.g. for popup flow)
58
+ * If provided, this will be called instead of window.location.href assignment
59
+ */
60
+ onRedirect?: (url: string) => void;
61
+ }
62
+
63
+ export interface McpConnection {
64
+ sessionId: string;
65
+ serverId: string;
66
+ serverName: string;
67
+ serverUrl?: string;
68
+ transport?: string;
69
+ state: McpConnectionState;
70
+ tools: ToolInfo[];
71
+ error?: string;
72
+ connectedAt?: Date;
73
+ }
74
+
75
+ export interface McpClient {
76
+ /**
77
+ * All connections
78
+ */
79
+ connections: McpConnection[];
80
+
81
+ /**
82
+ * SSE connection status
83
+ */
84
+ status: 'connecting' | 'connected' | 'disconnected' | 'error';
85
+
86
+ /**
87
+ * Whether initializing
88
+ */
89
+ isInitializing: boolean;
90
+
91
+ /**
92
+ * Connect to an MCP server
93
+ */
94
+ connect: (params: {
95
+ serverId: string;
96
+ serverName: string;
97
+ serverUrl: string;
98
+ callbackUrl: string;
99
+ transportType?: 'sse' | 'streamable_http';
100
+ }) => Promise<string>;
101
+
102
+ /**
103
+ * Disconnect from an MCP server
104
+ */
105
+ disconnect: (sessionId: string) => Promise<void>;
106
+
107
+ /**
108
+ * Get connection by session ID
109
+ */
110
+ getConnection: (sessionId: string) => McpConnection | undefined;
111
+
112
+ /**
113
+ * Get connection by server ID
114
+ */
115
+ getConnectionByServerId: (serverId: string) => McpConnection | undefined;
116
+
117
+ /**
118
+ * Check if server is connected
119
+ */
120
+ isServerConnected: (serverId: string) => boolean;
121
+
122
+ /**
123
+ * Get tools for a session
124
+ */
125
+ getTools: (sessionId: string) => ToolInfo[];
126
+
127
+ /**
128
+ * Refresh all connections
129
+ */
130
+ refresh: () => Promise<void>;
131
+
132
+ /**
133
+ * Manually connect SSE
134
+ */
135
+ connectSSE: () => void;
136
+
137
+ /**
138
+ * Manually disconnect SSE
139
+ */
140
+ disconnectSSE: () => void;
141
+
142
+ /**
143
+ * Complete OAuth authorization
144
+ */
145
+ finishAuth: (sessionId: string, code: string) => Promise<FinishAuthResult>;
146
+
147
+ /**
148
+ * Call a tool from a session
149
+ */
150
+ callTool: (
151
+ sessionId: string,
152
+ toolName: string,
153
+ toolArgs: Record<string, unknown>
154
+ ) => Promise<unknown>;
155
+
156
+ /**
157
+ * List available tools for a session
158
+ */
159
+ listTools: (sessionId: string) => Promise<ListToolsRpcResult>;
160
+
161
+ /**
162
+ * List available prompts for a session
163
+ */
164
+ listPrompts: (sessionId: string) => Promise<ListPromptsResult>;
165
+
166
+ /**
167
+ * Get a specific prompt with arguments
168
+ */
169
+ getPrompt: (sessionId: string, name: string, args?: Record<string, string>) => Promise<unknown>;
170
+
171
+ /**
172
+ * List available resources for a session
173
+ */
174
+ listResources: (sessionId: string) => Promise<ListResourcesResult>;
175
+
176
+ /**
177
+ * Read a specific resource
178
+ */
179
+ readResource: (sessionId: string, uri: string) => Promise<unknown>;
180
+ }
181
+
182
+ /**
183
+ * React hook for MCP connection management with SSE
184
+ */
185
+ export function useMcp(options: UseMcpOptions): McpClient {
186
+ const {
187
+ url,
188
+ identity,
189
+ authToken,
190
+ autoConnect = true,
191
+ autoInitialize = true,
192
+ onConnectionEvent,
193
+ onLog,
194
+ onRedirect,
195
+ } = options;
196
+
197
+ const clientRef = useRef<SSEClient | null>(null);
198
+ const isMountedRef = useRef(true);
199
+
200
+ const [connections, setConnections] = useState<McpConnection[]>([]);
201
+ const [status, setStatus] = useState<'connecting' | 'connected' | 'disconnected' | 'error'>(
202
+ 'disconnected'
203
+ );
204
+ const [isInitializing, setIsInitializing] = useState(false);
205
+
206
+ /**
207
+ * Initialize SSE client
208
+ */
209
+ useEffect(() => {
210
+ isMountedRef.current = true;
211
+
212
+ const clientOptions: SSEClientOptions = {
213
+ url,
214
+ identity,
215
+ authToken,
216
+ onConnectionEvent: (event) => {
217
+ // Update local state based on event
218
+ updateConnectionsFromEvent(event);
219
+
220
+ // Call user callback
221
+ onConnectionEvent?.(event);
222
+ },
223
+ onObservabilityEvent: (event) => {
224
+ onLog?.(event.level || 'info', event.message || event.displayMessage || 'No message', event.metadata);
225
+ },
226
+ onStatusChange: (newStatus) => {
227
+ if (isMountedRef.current) {
228
+ setStatus(newStatus);
229
+ }
230
+ },
231
+ };
232
+
233
+ const client = new SSEClient(clientOptions);
234
+ clientRef.current = client;
235
+
236
+ if (autoConnect) {
237
+ client.connect();
238
+
239
+ if (autoInitialize) {
240
+ loadSessions();
241
+ }
242
+ }
243
+
244
+ return () => {
245
+ isMountedRef.current = false;
246
+ client.disconnect();
247
+ };
248
+ }, [url, identity, authToken, autoConnect, autoInitialize]);
249
+
250
+ /**
251
+ * Update connections based on event
252
+ */
253
+ const updateConnectionsFromEvent = useCallback((event: McpConnectionEvent) => {
254
+ if (!isMountedRef.current) return;
255
+
256
+ setConnections((prev: McpConnection[]) => {
257
+ switch (event.type) {
258
+ case 'state_changed': {
259
+ const existing = prev.find((c: McpConnection) => c.sessionId === event.sessionId);
260
+ if (existing) {
261
+ return prev.map((c: McpConnection) =>
262
+ c.sessionId === event.sessionId ? { ...c, state: event.state } : c
263
+ );
264
+ } else {
265
+ return [
266
+ ...prev,
267
+ {
268
+ sessionId: event.sessionId,
269
+ serverId: event.serverId,
270
+ serverName: event.serverName,
271
+ state: event.state,
272
+ tools: [],
273
+ },
274
+ ];
275
+ }
276
+ }
277
+
278
+ case 'tools_discovered': {
279
+ return prev.map((c: McpConnection) =>
280
+ c.sessionId === event.sessionId ? { ...c, tools: event.tools, state: 'READY' } : c
281
+ );
282
+ }
283
+
284
+ case 'auth_required': {
285
+ // Handle OAuth redirect
286
+ if (event.authUrl) {
287
+ onLog?.('info', `OAuth required - redirecting to ${event.authUrl}`, { authUrl: event.authUrl });
288
+
289
+ if (onRedirect) {
290
+ onRedirect(event.authUrl);
291
+ } else if (typeof window !== 'undefined') {
292
+ window.location.href = event.authUrl;
293
+ }
294
+ }
295
+ return prev.map((c: McpConnection) =>
296
+ c.sessionId === event.sessionId ? { ...c, state: 'AUTHENTICATING' } : c
297
+ );
298
+ }
299
+
300
+ case 'error': {
301
+ return prev.map((c: McpConnection) =>
302
+ c.sessionId === event.sessionId ? { ...c, state: 'FAILED', error: event.error } : c
303
+ );
304
+ }
305
+
306
+ case 'disconnected': {
307
+ return prev.filter((c: McpConnection) => c.sessionId !== event.sessionId);
308
+ }
309
+
310
+ default:
311
+ return prev;
312
+ }
313
+ });
314
+ }, [onLog]);
315
+
316
+ /**
317
+ * Load sessions from server
318
+ */
319
+ const loadSessions = useCallback(async () => {
320
+ if (!clientRef.current) return;
321
+
322
+ try {
323
+ setIsInitializing(true);
324
+
325
+ const result = await clientRef.current.getSessions();
326
+ const sessions = result.sessions || [];
327
+
328
+ // Initialize connections
329
+ if (isMountedRef.current) {
330
+ setConnections(
331
+ sessions.map((s: SessionInfo) => ({
332
+ sessionId: s.sessionId,
333
+ serverId: s.serverId ?? 'unknown',
334
+ serverName: s.serverName ?? 'Unknown Server',
335
+ serverUrl: s.serverUrl,
336
+ transport: s.transport,
337
+ state: 'VALIDATING' as McpConnectionState,
338
+ tools: [],
339
+ }))
340
+ );
341
+ }
342
+
343
+ // Validate each session in parallel
344
+ await Promise.all(
345
+ sessions.map(async (session: SessionInfo) => {
346
+ if (clientRef.current) {
347
+ try {
348
+ await clientRef.current.restoreSession(session.sessionId);
349
+ } catch (error) {
350
+ console.error(`[useMcp] Failed to validate session ${session.sessionId}:`, error);
351
+ }
352
+ }
353
+ })
354
+ );
355
+ } catch (error) {
356
+ console.error('[useMcp] Failed to load sessions:', error);
357
+ onLog?.('error', 'Failed to load sessions', { error });
358
+ } finally {
359
+ if (isMountedRef.current) {
360
+ setIsInitializing(false);
361
+ }
362
+ }
363
+ }, [onLog]);
364
+
365
+ /**
366
+ * Connect to an MCP server
367
+ */
368
+ const connect = useCallback(
369
+ async (params: {
370
+ serverId: string;
371
+ serverName: string;
372
+ serverUrl: string;
373
+ callbackUrl: string;
374
+ transportType?: 'sse' | 'streamable_http';
375
+ }): Promise<string> => {
376
+ if (!clientRef.current) {
377
+ throw new Error('SSE client not initialized');
378
+ }
379
+
380
+ const result = await clientRef.current.connectToServer(params);
381
+ return result.sessionId;
382
+ },
383
+ []
384
+ );
385
+
386
+ /**
387
+ * Disconnect from an MCP server
388
+ */
389
+ const disconnect = useCallback(async (sessionId: string): Promise<void> => {
390
+ if (!clientRef.current) {
391
+ throw new Error('SSE client not initialized');
392
+ }
393
+
394
+ await clientRef.current.disconnectFromServer(sessionId);
395
+
396
+ // Remove from local state
397
+ if (isMountedRef.current) {
398
+ setConnections((prev: McpConnection[]) => prev.filter((c: McpConnection) => c.sessionId !== sessionId));
399
+ }
400
+ }, []);
401
+
402
+ /**
403
+ * Refresh all connections
404
+ */
405
+ const refresh = useCallback(async () => {
406
+ await loadSessions();
407
+ }, [loadSessions]);
408
+
409
+ /**
410
+ * Manually connect SSE
411
+ */
412
+ const connectSSE = useCallback(() => {
413
+ clientRef.current?.connect();
414
+ }, []);
415
+
416
+ /**
417
+ * Manually disconnect SSE
418
+ */
419
+ const disconnectSSE = useCallback(() => {
420
+ clientRef.current?.disconnect();
421
+ }, []);
422
+
423
+ /**
424
+ * Complete OAuth authorization
425
+ */
426
+ const finishAuth = useCallback(async (sessionId: string, code: string): Promise<FinishAuthResult> => {
427
+ if (!clientRef.current) {
428
+ throw new Error('SSE client not initialized');
429
+ }
430
+
431
+ return await clientRef.current.finishAuth(sessionId, code);
432
+ }, []);
433
+
434
+ /**
435
+ * Call a tool
436
+ */
437
+ const callTool = useCallback(
438
+ async (
439
+ sessionId: string,
440
+ toolName: string,
441
+ toolArgs: Record<string, unknown>
442
+ ): Promise<unknown> => {
443
+ if (!clientRef.current) {
444
+ throw new Error('SSE client not initialized');
445
+ }
446
+
447
+ return await clientRef.current.callTool(sessionId, toolName, toolArgs);
448
+ },
449
+ []
450
+ );
451
+
452
+ /**
453
+ * List tools (refresh tool list)
454
+ */
455
+ const listTools = useCallback(async (sessionId: string): Promise<ListToolsRpcResult> => {
456
+ if (!clientRef.current) {
457
+ throw new Error('SSE client not initialized');
458
+ }
459
+
460
+ return await clientRef.current.listTools(sessionId);
461
+ }, []);
462
+
463
+ /**
464
+ * List prompts
465
+ */
466
+ const listPrompts = useCallback(async (sessionId: string): Promise<ListPromptsResult> => {
467
+ if (!clientRef.current) {
468
+ throw new Error('SSE client not initialized');
469
+ }
470
+
471
+ return await clientRef.current.listPrompts(sessionId);
472
+ }, []);
473
+
474
+ /**
475
+ * Get a specific prompt
476
+ */
477
+ const getPrompt = useCallback(
478
+ async (sessionId: string, name: string, args?: Record<string, string>): Promise<unknown> => {
479
+ if (!clientRef.current) {
480
+ throw new Error('SSE client not initialized');
481
+ }
482
+
483
+ return await clientRef.current.getPrompt(sessionId, name, args);
484
+ },
485
+ []
486
+ );
487
+
488
+ /**
489
+ * List resources
490
+ */
491
+ const listResources = useCallback(async (sessionId: string): Promise<ListResourcesResult> => {
492
+ if (!clientRef.current) {
493
+ throw new Error('SSE client not initialized');
494
+ }
495
+
496
+ return await clientRef.current.listResources(sessionId);
497
+ }, []);
498
+
499
+ /**
500
+ * Read a specific resource
501
+ */
502
+ const readResource = useCallback(async (sessionId: string, uri: string): Promise<unknown> => {
503
+ if (!clientRef.current) {
504
+ throw new Error('SSE client not initialized');
505
+ }
506
+
507
+ return await clientRef.current.readResource(sessionId, uri);
508
+ }, []);
509
+
510
+ // Utility functions
511
+ const getConnection = useCallback(
512
+ (sessionId: string) => connections.find((c: McpConnection) => c.sessionId === sessionId),
513
+ [connections]
514
+ );
515
+
516
+ const getConnectionByServerId = useCallback(
517
+ (serverId: string) => connections.find((c: McpConnection) => c.serverId === serverId),
518
+ [connections]
519
+ );
520
+
521
+ const isServerConnected = useCallback(
522
+ (serverId: string) => {
523
+ const conn = getConnectionByServerId(serverId);
524
+ return conn?.state === 'CONNECTED';
525
+ },
526
+ [getConnectionByServerId]
527
+ );
528
+
529
+ const getTools = useCallback(
530
+ (sessionId: string) => {
531
+ const conn = getConnection(sessionId);
532
+ return conn?.tools || [];
533
+ },
534
+ [getConnection]
535
+ );
536
+
537
+ return {
538
+ connections,
539
+ status,
540
+ isInitializing,
541
+ connect,
542
+ disconnect,
543
+ getConnection,
544
+ getConnectionByServerId,
545
+ isServerConnected,
546
+ getTools,
547
+ refresh,
548
+ connectSSE,
549
+ disconnectSSE,
550
+ finishAuth,
551
+ callTool,
552
+ listTools,
553
+ listPrompts,
554
+ getPrompt,
555
+ listResources,
556
+ readResource,
557
+ };
558
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * MCP Redis Client Package - Vue.js
3
+ * Vue.js client-side exports for MCP connection management
4
+ */
5
+
6
+ // Vue Composable
7
+ export { useMcp, type UseMcpOptions, type McpClient, type McpConnection } from './useMcp';
8
+
9
+ // Re-export shared types and client from main entry
10
+ export * from '../index';