@mcp-ts/sdk 1.3.1 → 1.3.3

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 (63) hide show
  1. package/README.md +371 -290
  2. package/dist/adapters/agui-adapter.d.mts +3 -3
  3. package/dist/adapters/agui-adapter.d.ts +3 -3
  4. package/dist/adapters/agui-middleware.d.mts +3 -3
  5. package/dist/adapters/agui-middleware.d.ts +3 -3
  6. package/dist/adapters/ai-adapter.d.mts +3 -3
  7. package/dist/adapters/ai-adapter.d.ts +3 -3
  8. package/dist/adapters/langchain-adapter.d.mts +3 -3
  9. package/dist/adapters/langchain-adapter.d.ts +3 -3
  10. package/dist/adapters/mastra-adapter.d.mts +3 -3
  11. package/dist/adapters/mastra-adapter.d.ts +3 -3
  12. package/dist/client/index.d.mts +10 -66
  13. package/dist/client/index.d.ts +10 -66
  14. package/dist/client/index.js +91 -173
  15. package/dist/client/index.js.map +1 -1
  16. package/dist/client/index.mjs +91 -173
  17. package/dist/client/index.mjs.map +1 -1
  18. package/dist/client/react.d.mts +15 -5
  19. package/dist/client/react.d.ts +15 -5
  20. package/dist/client/react.js +130 -182
  21. package/dist/client/react.js.map +1 -1
  22. package/dist/client/react.mjs +130 -182
  23. package/dist/client/react.mjs.map +1 -1
  24. package/dist/client/vue.d.mts +27 -7
  25. package/dist/client/vue.d.ts +27 -7
  26. package/dist/client/vue.js +131 -182
  27. package/dist/client/vue.js.map +1 -1
  28. package/dist/client/vue.mjs +131 -182
  29. package/dist/client/vue.mjs.map +1 -1
  30. package/dist/{events-BgeztGYZ.d.mts → events-CK3N--3g.d.mts} +2 -0
  31. package/dist/{events-BgeztGYZ.d.ts → events-CK3N--3g.d.ts} +2 -0
  32. package/dist/index.d.mts +3 -3
  33. package/dist/index.d.ts +3 -3
  34. package/dist/index.js +224 -258
  35. package/dist/index.js.map +1 -1
  36. package/dist/index.mjs +224 -258
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/{multi-session-client-CxogNckF.d.mts → multi-session-client-DzjmT7FX.d.mts} +4 -10
  39. package/dist/{multi-session-client-cox_WXUj.d.ts → multi-session-client-FAFpUzZ4.d.ts} +4 -10
  40. package/dist/server/index.d.mts +18 -23
  41. package/dist/server/index.d.ts +18 -23
  42. package/dist/server/index.js +133 -85
  43. package/dist/server/index.js.map +1 -1
  44. package/dist/server/index.mjs +133 -85
  45. package/dist/server/index.mjs.map +1 -1
  46. package/dist/shared/index.d.mts +3 -3
  47. package/dist/shared/index.d.ts +3 -3
  48. package/dist/shared/index.js.map +1 -1
  49. package/dist/shared/index.mjs.map +1 -1
  50. package/dist/{types-CLccx9wW.d.mts → types-CW6lghof.d.mts} +6 -0
  51. package/dist/{types-CLccx9wW.d.ts → types-CW6lghof.d.ts} +6 -0
  52. package/package.json +1 -1
  53. package/src/client/core/sse-client.ts +354 -493
  54. package/src/client/react/index.ts +16 -16
  55. package/src/client/react/use-mcp-apps.tsx +214 -214
  56. package/src/client/react/use-mcp.ts +84 -19
  57. package/src/client/vue/use-mcp.ts +119 -44
  58. package/src/server/handlers/nextjs-handler.ts +207 -217
  59. package/src/server/handlers/sse-handler.ts +14 -0
  60. package/src/server/mcp/oauth-client.ts +48 -46
  61. package/src/server/storage/types.ts +12 -5
  62. package/src/shared/events.ts +2 -0
  63. package/src/shared/types.ts +6 -0
@@ -16,7 +16,7 @@ import type {
16
16
  SessionInfo,
17
17
  } from '../../shared/types';
18
18
 
19
- export interface UseMcpOptions {
19
+ export interface UseMcpOptions {
20
20
  /**
21
21
  * SSE endpoint URL
22
22
  */
@@ -53,12 +53,24 @@ export interface UseMcpOptions {
53
53
  * Debug logging callback
54
54
  */
55
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
- }
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
+ * Request timeout in milliseconds
64
+ * @default 60000
65
+ */
66
+ requestTimeout?: number;
67
+
68
+ /**
69
+ * Enable client debug logs.
70
+ * @default false
71
+ */
72
+ debug?: boolean;
73
+ }
62
74
 
63
75
  export interface McpConnection {
64
76
  sessionId: string;
@@ -68,8 +80,9 @@ export interface McpConnection {
68
80
  transport?: string;
69
81
  state: McpConnectionState;
70
82
  tools: ToolInfo[];
83
+ authUrl?: string;
71
84
  error?: string;
72
- connectedAt?: Date;
85
+ createdAt?: Date;
73
86
  }
74
87
 
75
88
  export interface McpClient {
@@ -144,6 +157,11 @@ export interface McpClient {
144
157
  */
145
158
  finishAuth: (sessionId: string, code: string) => Promise<FinishAuthResult>;
146
159
 
160
+ /**
161
+ * Explicitly resume OAuth flow for an existing session
162
+ */
163
+ resumeAuth: (sessionId: string) => Promise<void>;
164
+
147
165
  /**
148
166
  * Call a tool from a session
149
167
  */
@@ -173,11 +191,16 @@ export interface McpClient {
173
191
  */
174
192
  listResources: (sessionId: string) => Promise<ListResourcesResult>;
175
193
 
176
- /**
177
- * Read a specific resource
178
- */
179
- readResource: (sessionId: string, uri: string) => Promise<unknown>;
180
- }
194
+ /**
195
+ * Read a specific resource
196
+ */
197
+ readResource: (sessionId: string, uri: string) => Promise<unknown>;
198
+
199
+ /**
200
+ * Access the underlying SSEClient instance (for advanced usage like AppHost)
201
+ */
202
+ sseClient: SSEClient | null;
203
+ }
181
204
 
182
205
  /**
183
206
  * Vue Composable for MCP connection management with SSE
@@ -197,6 +220,7 @@ export function useMcp(options: UseMcpOptions): McpClient {
197
220
  // Use shallowRef for client instance as it doesn't need deep reactivity
198
221
  const clientRef = shallowRef<SSEClient | null>(null);
199
222
  const isMountedRef = ref(true);
223
+ const suppressAuthRedirectSessions = ref(new Set<string>());
200
224
 
201
225
  const connections = ref<McpConnection[]>([]);
202
226
  const status = ref<'connecting' | 'connected' | 'disconnected' | 'error'>('disconnected');
@@ -205,21 +229,47 @@ export function useMcp(options: UseMcpOptions): McpClient {
205
229
  /**
206
230
  * Update connections based on event
207
231
  */
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 };
232
+ const updateConnectionsFromEvent = (event: McpConnectionEvent) => {
233
+ if (!isMountedRef.value) return;
234
+
235
+ const isTransientReconnectState = (state: McpConnectionState): boolean =>
236
+ state === 'INITIALIZING' ||
237
+ state === 'VALIDATING' ||
238
+ state === 'RECONNECTING' ||
239
+ state === 'CONNECTING' ||
240
+ state === 'CONNECTED' ||
241
+ state === 'DISCOVERING';
242
+
243
+ switch (event.type) {
244
+ case 'state_changed': {
245
+ const existing = connections.value.find((c) => c.sessionId === event.sessionId);
246
+ if (existing) {
247
+ // In stateless per-request transport, tool calls can emit transient reconnect states.
248
+ // Keep READY sticky to avoid UI flicker from READY -> CONNECTING -> CONNECTED.
249
+ const nextState =
250
+ existing.state === 'READY' && isTransientReconnectState(event.state)
251
+ ? existing.state
252
+ : event.state;
253
+
254
+ const index = connections.value.indexOf(existing);
255
+ connections.value[index] = {
256
+ ...existing,
257
+ state: nextState,
258
+ // update createdAt if present in event, otherwise keep existing
259
+ createdAt: event.createdAt ? new Date(event.createdAt) : existing.createdAt
260
+ };
217
261
  } else {
262
+ // Fix: Don't add back disconnected sessions that were just removed
263
+ if (event.state === 'DISCONNECTED') {
264
+ break;
265
+ }
266
+
218
267
  connections.value = [...connections.value, {
219
268
  sessionId: event.sessionId,
220
269
  serverId: event.serverId,
221
270
  serverName: event.serverName,
222
271
  state: event.state,
272
+ createdAt: event.createdAt ? new Date(event.createdAt) : undefined,
223
273
  tools: [],
224
274
  }];
225
275
  }
@@ -239,15 +289,18 @@ export function useMcp(options: UseMcpOptions): McpClient {
239
289
  if (event.authUrl) {
240
290
  onLog?.('info', `OAuth required - redirecting to ${event.authUrl}`, { authUrl: event.authUrl });
241
291
 
242
- if (onRedirect) {
243
- onRedirect(event.authUrl);
244
- } else if (typeof window !== 'undefined') {
245
- window.location.href = event.authUrl;
292
+ // Suppress redirects/popups for background auto-restore on page load.
293
+ if (!suppressAuthRedirectSessions.value.has(event.sessionId)) {
294
+ if (onRedirect) {
295
+ onRedirect(event.authUrl);
296
+ } else if (typeof window !== 'undefined') {
297
+ window.location.href = event.authUrl;
298
+ }
246
299
  }
247
300
  }
248
301
  const index = connections.value.findIndex((c) => c.sessionId === event.sessionId);
249
302
  if (index !== -1) {
250
- connections.value[index] = { ...connections.value[index], state: 'AUTHENTICATING' };
303
+ connections.value[index] = { ...connections.value[index], state: 'AUTHENTICATING', authUrl: event.authUrl };
251
304
  }
252
305
  break;
253
306
  }
@@ -287,7 +340,8 @@ export function useMcp(options: UseMcpOptions): McpClient {
287
340
  serverName: s.serverName ?? 'Unknown Server',
288
341
  serverUrl: s.serverUrl,
289
342
  transport: s.transport,
290
- state: 'VALIDATING' as McpConnectionState,
343
+ state: (s.active === false ? 'AUTHENTICATING' : 'VALIDATING') as McpConnectionState,
344
+ createdAt: new Date(s.createdAt),
291
345
  tools: [],
292
346
  }));
293
347
  }
@@ -297,9 +351,16 @@ export function useMcp(options: UseMcpOptions): McpClient {
297
351
  sessions.map(async (session: SessionInfo) => {
298
352
  if (clientRef.value) {
299
353
  try {
354
+ // Pending auth sessions should not auto-trigger popup/redirect on reload.
355
+ if (session.active === false) {
356
+ return;
357
+ }
358
+ suppressAuthRedirectSessions.value.add(session.sessionId);
300
359
  await clientRef.value.restoreSession(session.sessionId);
301
360
  } catch (error) {
302
361
  console.error(`[useMcp] Failed to validate session ${session.sessionId}:`, error);
362
+ } finally {
363
+ suppressAuthRedirectSessions.value.delete(session.sessionId);
303
364
  }
304
365
  }
305
366
  })
@@ -323,10 +384,10 @@ export function useMcp(options: UseMcpOptions): McpClient {
323
384
  clientRef.value.disconnect();
324
385
  }
325
386
 
326
- const clientOptions: SSEClientOptions = {
327
- url,
328
- identity,
329
- authToken,
387
+ const clientOptions: SSEClientOptions = {
388
+ url,
389
+ identity,
390
+ authToken,
330
391
  onConnectionEvent: (event) => {
331
392
  // Update local state based on event
332
393
  updateConnectionsFromEvent(event);
@@ -337,12 +398,13 @@ export function useMcp(options: UseMcpOptions): McpClient {
337
398
  onObservabilityEvent: (event) => {
338
399
  onLog?.(event.level || 'info', event.message || event.displayMessage || 'No message', event.metadata);
339
400
  },
340
- onStatusChange: (newStatus) => {
341
- if (isMountedRef.value) {
342
- status.value = newStatus;
343
- }
344
- },
345
- };
401
+ onStatusChange: (newStatus) => {
402
+ if (isMountedRef.value) {
403
+ status.value = newStatus;
404
+ }
405
+ },
406
+ debug: options.debug,
407
+ };
346
408
 
347
409
  const client = new SSEClient(clientOptions);
348
410
  clientRef.value = client;
@@ -432,6 +494,17 @@ export function useMcp(options: UseMcpOptions): McpClient {
432
494
  return await clientRef.value.finishAuth(sessionId, code);
433
495
  };
434
496
 
497
+ /**
498
+ * Explicit user action to resume OAuth for an existing pending session.
499
+ */
500
+ const resumeAuth = async (sessionId: string): Promise<void> => {
501
+ if (!clientRef.value) {
502
+ throw new Error('SSE client not initialized');
503
+ }
504
+ suppressAuthRedirectSessions.value.delete(sessionId);
505
+ await clientRef.value.restoreSession(sessionId);
506
+ };
507
+
435
508
  /**
436
509
  * Call a tool
437
510
  */
@@ -532,11 +605,13 @@ export function useMcp(options: UseMcpOptions): McpClient {
532
605
  connectSSE,
533
606
  disconnectSSE,
534
607
  finishAuth,
608
+ resumeAuth,
535
609
  callTool,
536
610
  listTools,
537
- listPrompts,
538
- getPrompt,
539
- listResources,
540
- readResource,
541
- };
542
- }
611
+ listPrompts,
612
+ getPrompt,
613
+ listResources,
614
+ readResource,
615
+ sseClient: clientRef.value,
616
+ };
617
+ }