@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,542 @@
1
+ /**
2
+ * useMcp Vue Composable
3
+ * Manages MCP connections with SSE-based real-time updates
4
+ * Based on Cloudflare's agents pattern
5
+ */
6
+
7
+ import { ref, onMounted, onUnmounted, watch, computed, shallowRef } from 'vue';
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 (Represents a Reactive Ref)
78
+ */
79
+ connections: { value: McpConnection[] };
80
+
81
+ /**
82
+ * SSE connection status (Represents a Reactive Ref)
83
+ */
84
+ status: { value: 'connecting' | 'connected' | 'disconnected' | 'error' };
85
+
86
+ /**
87
+ * Whether initializing (Represents a Reactive Ref)
88
+ */
89
+ isInitializing: { value: 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
+ * Vue Composable 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
+ // Use shallowRef for client instance as it doesn't need deep reactivity
198
+ const clientRef = shallowRef<SSEClient | null>(null);
199
+ const isMountedRef = ref(true);
200
+
201
+ const connections = ref<McpConnection[]>([]);
202
+ const status = ref<'connecting' | 'connected' | 'disconnected' | 'error'>('disconnected');
203
+ const isInitializing = ref(false);
204
+
205
+ /**
206
+ * Update connections based on event
207
+ */
208
+ const updateConnectionsFromEvent = (event: McpConnectionEvent) => {
209
+ if (!isMountedRef.value) return;
210
+
211
+ switch (event.type) {
212
+ case 'state_changed': {
213
+ const existing = connections.value.find((c) => c.sessionId === event.sessionId);
214
+ if (existing) {
215
+ const index = connections.value.indexOf(existing);
216
+ connections.value[index] = { ...existing, state: event.state };
217
+ } else {
218
+ connections.value = [...connections.value, {
219
+ sessionId: event.sessionId,
220
+ serverId: event.serverId,
221
+ serverName: event.serverName,
222
+ state: event.state,
223
+ tools: [],
224
+ }];
225
+ }
226
+ break;
227
+ }
228
+
229
+ case 'tools_discovered': {
230
+ const index = connections.value.findIndex((c) => c.sessionId === event.sessionId);
231
+ if (index !== -1) {
232
+ connections.value[index] = { ...connections.value[index], tools: event.tools, state: 'READY' };
233
+ }
234
+ break;
235
+ }
236
+
237
+ case 'auth_required': {
238
+ // Handle OAuth redirect
239
+ if (event.authUrl) {
240
+ onLog?.('info', `OAuth required - redirecting to ${event.authUrl}`, { authUrl: event.authUrl });
241
+
242
+ if (onRedirect) {
243
+ onRedirect(event.authUrl);
244
+ } else if (typeof window !== 'undefined') {
245
+ window.location.href = event.authUrl;
246
+ }
247
+ }
248
+ const index = connections.value.findIndex((c) => c.sessionId === event.sessionId);
249
+ if (index !== -1) {
250
+ connections.value[index] = { ...connections.value[index], state: 'AUTHENTICATING' };
251
+ }
252
+ break;
253
+ }
254
+
255
+ case 'error': {
256
+ const index = connections.value.findIndex((c) => c.sessionId === event.sessionId);
257
+ if (index !== -1) {
258
+ connections.value[index] = { ...connections.value[index], state: 'FAILED', error: event.error };
259
+ }
260
+ break;
261
+ }
262
+
263
+ case 'disconnected': {
264
+ connections.value = connections.value.filter((c) => c.sessionId !== event.sessionId);
265
+ break;
266
+ }
267
+ }
268
+ };
269
+
270
+ /**
271
+ * Load sessions from server
272
+ */
273
+ const loadSessions = async () => {
274
+ if (!clientRef.value) return;
275
+
276
+ try {
277
+ isInitializing.value = true;
278
+
279
+ const result = await clientRef.value.getSessions();
280
+ const sessions = result.sessions || [];
281
+
282
+ // Initialize connections
283
+ if (isMountedRef.value) {
284
+ connections.value = sessions.map((s: SessionInfo) => ({
285
+ sessionId: s.sessionId,
286
+ serverId: s.serverId ?? 'unknown',
287
+ serverName: s.serverName ?? 'Unknown Server',
288
+ serverUrl: s.serverUrl,
289
+ transport: s.transport,
290
+ state: 'VALIDATING' as McpConnectionState,
291
+ tools: [],
292
+ }));
293
+ }
294
+
295
+ // Validate each session in parallel
296
+ await Promise.all(
297
+ sessions.map(async (session: SessionInfo) => {
298
+ if (clientRef.value) {
299
+ try {
300
+ await clientRef.value.restoreSession(session.sessionId);
301
+ } catch (error) {
302
+ console.error(`[useMcp] Failed to validate session ${session.sessionId}:`, error);
303
+ }
304
+ }
305
+ })
306
+ );
307
+ } catch (error) {
308
+ console.error('[useMcp] Failed to load sessions:', error);
309
+ onLog?.('error', 'Failed to load sessions', { error });
310
+ } finally {
311
+ if (isMountedRef.value) {
312
+ isInitializing.value = false;
313
+ }
314
+ }
315
+ };
316
+
317
+ /**
318
+ * Initialize SSE client
319
+ */
320
+ const initClient = () => {
321
+ // Disconnect existing if any
322
+ if (clientRef.value) {
323
+ clientRef.value.disconnect();
324
+ }
325
+
326
+ const clientOptions: SSEClientOptions = {
327
+ url,
328
+ identity,
329
+ authToken,
330
+ onConnectionEvent: (event) => {
331
+ // Update local state based on event
332
+ updateConnectionsFromEvent(event);
333
+
334
+ // Call user callback
335
+ onConnectionEvent?.(event);
336
+ },
337
+ onObservabilityEvent: (event) => {
338
+ onLog?.(event.level || 'info', event.message || event.displayMessage || 'No message', event.metadata);
339
+ },
340
+ onStatusChange: (newStatus) => {
341
+ if (isMountedRef.value) {
342
+ status.value = newStatus;
343
+ }
344
+ },
345
+ };
346
+
347
+ const client = new SSEClient(clientOptions);
348
+ clientRef.value = client;
349
+
350
+ if (autoConnect) {
351
+ client.connect();
352
+
353
+ if (autoInitialize) {
354
+ loadSessions();
355
+ }
356
+ }
357
+ };
358
+
359
+ onMounted(() => {
360
+ isMountedRef.value = true;
361
+ initClient();
362
+ });
363
+
364
+ onUnmounted(() => {
365
+ isMountedRef.value = false;
366
+ clientRef.value?.disconnect();
367
+ });
368
+
369
+ /**
370
+ * Connect to an MCP server
371
+ */
372
+ const connect = async (params: {
373
+ serverId: string;
374
+ serverName: string;
375
+ serverUrl: string;
376
+ callbackUrl: string;
377
+ transportType?: 'sse' | 'streamable_http';
378
+ }): Promise<string> => {
379
+ if (!clientRef.value) {
380
+ throw new Error('SSE client not initialized');
381
+ }
382
+
383
+ const result = await clientRef.value.connectToServer(params);
384
+ return result.sessionId;
385
+ };
386
+
387
+ /**
388
+ * Disconnect from an MCP server
389
+ */
390
+ const disconnect = async (sessionId: string): Promise<void> => {
391
+ if (!clientRef.value) {
392
+ throw new Error('SSE client not initialized');
393
+ }
394
+
395
+ await clientRef.value.disconnectFromServer(sessionId);
396
+
397
+ // Remove from local state
398
+ if (isMountedRef.value) {
399
+ connections.value = connections.value.filter((c) => c.sessionId !== sessionId);
400
+ }
401
+ };
402
+
403
+ /**
404
+ * Refresh all connections
405
+ */
406
+ const refresh = async () => {
407
+ await loadSessions();
408
+ };
409
+
410
+ /**
411
+ * Manually connect SSE
412
+ */
413
+ const connectSSE = () => {
414
+ clientRef.value?.connect();
415
+ };
416
+
417
+ /**
418
+ * Manually disconnect SSE
419
+ */
420
+ const disconnectSSE = () => {
421
+ clientRef.value?.disconnect();
422
+ };
423
+
424
+ /**
425
+ * Complete OAuth authorization
426
+ */
427
+ const finishAuth = async (sessionId: string, code: string): Promise<FinishAuthResult> => {
428
+ if (!clientRef.value) {
429
+ throw new Error('SSE client not initialized');
430
+ }
431
+
432
+ return await clientRef.value.finishAuth(sessionId, code);
433
+ };
434
+
435
+ /**
436
+ * Call a tool
437
+ */
438
+ const callTool = async (
439
+ sessionId: string,
440
+ toolName: string,
441
+ toolArgs: Record<string, unknown>
442
+ ): Promise<unknown> => {
443
+ if (!clientRef.value) {
444
+ throw new Error('SSE client not initialized');
445
+ }
446
+
447
+ return await clientRef.value.callTool(sessionId, toolName, toolArgs);
448
+ };
449
+
450
+ /**
451
+ * List tools (refresh tool list)
452
+ */
453
+ const listTools = async (sessionId: string): Promise<ListToolsRpcResult> => {
454
+ if (!clientRef.value) {
455
+ throw new Error('SSE client not initialized');
456
+ }
457
+
458
+ return await clientRef.value.listTools(sessionId);
459
+ };
460
+
461
+ /**
462
+ * List prompts
463
+ */
464
+ const listPrompts = async (sessionId: string): Promise<ListPromptsResult> => {
465
+ if (!clientRef.value) {
466
+ throw new Error('SSE client not initialized');
467
+ }
468
+
469
+ return await clientRef.value.listPrompts(sessionId);
470
+ };
471
+
472
+ /**
473
+ * Get a specific prompt
474
+ */
475
+ const getPrompt = async (sessionId: string, name: string, args?: Record<string, string>): Promise<unknown> => {
476
+ if (!clientRef.value) {
477
+ throw new Error('SSE client not initialized');
478
+ }
479
+
480
+ return await clientRef.value.getPrompt(sessionId, name, args);
481
+ };
482
+
483
+ /**
484
+ * List resources
485
+ */
486
+ const listResources = async (sessionId: string): Promise<ListResourcesResult> => {
487
+ if (!clientRef.value) {
488
+ throw new Error('SSE client not initialized');
489
+ }
490
+
491
+ return await clientRef.value.listResources(sessionId);
492
+ };
493
+
494
+ /**
495
+ * Read a specific resource
496
+ */
497
+ const readResource = async (sessionId: string, uri: string): Promise<unknown> => {
498
+ if (!clientRef.value) {
499
+ throw new Error('SSE client not initialized');
500
+ }
501
+
502
+ return await clientRef.value.readResource(sessionId, uri);
503
+ };
504
+
505
+ // Utility functions
506
+ const getConnection = (sessionId: string) => connections.value.find((c) => c.sessionId === sessionId);
507
+
508
+ const getConnectionByServerId = (serverId: string) => connections.value.find((c) => c.serverId === serverId);
509
+
510
+ const isServerConnected = (serverId: string) => {
511
+ const conn = getConnectionByServerId(serverId);
512
+ return conn?.state === 'CONNECTED';
513
+ };
514
+
515
+ const getTools = (sessionId: string) => {
516
+ const conn = getConnection(sessionId);
517
+ return conn?.tools || [];
518
+ };
519
+
520
+ return {
521
+ // Return them as Ref objects so they can be destructured and stay reactive
522
+ connections: connections as unknown as { value: McpConnection[] },
523
+ status: status as unknown as { value: 'connecting' | 'connected' | 'disconnected' | 'error' },
524
+ isInitializing: isInitializing as unknown as { value: boolean },
525
+ connect,
526
+ disconnect,
527
+ getConnection,
528
+ getConnectionByServerId,
529
+ isServerConnected,
530
+ getTools,
531
+ refresh,
532
+ connectSSE,
533
+ disconnectSSE,
534
+ finishAuth,
535
+ callTool,
536
+ listTools,
537
+ listPrompts,
538
+ getPrompt,
539
+ listResources,
540
+ readResource,
541
+ };
542
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * MCP Redis
3
+ * Redis-backed MCP client with OAuth 2.1 and real-time SSE connections
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+
8
+ // Re-export everything from subpackages
9
+ export * from './server';
10
+ export * from './client';
11
+ export * from './shared';