@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.
- package/README.md +371 -290
- package/dist/adapters/agui-adapter.d.mts +3 -3
- package/dist/adapters/agui-adapter.d.ts +3 -3
- package/dist/adapters/agui-middleware.d.mts +3 -3
- package/dist/adapters/agui-middleware.d.ts +3 -3
- package/dist/adapters/ai-adapter.d.mts +3 -3
- package/dist/adapters/ai-adapter.d.ts +3 -3
- package/dist/adapters/langchain-adapter.d.mts +3 -3
- package/dist/adapters/langchain-adapter.d.ts +3 -3
- package/dist/adapters/mastra-adapter.d.mts +3 -3
- package/dist/adapters/mastra-adapter.d.ts +3 -3
- package/dist/client/index.d.mts +10 -66
- package/dist/client/index.d.ts +10 -66
- package/dist/client/index.js +91 -173
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +91 -173
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +15 -5
- package/dist/client/react.d.ts +15 -5
- package/dist/client/react.js +130 -182
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +130 -182
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +27 -7
- package/dist/client/vue.d.ts +27 -7
- package/dist/client/vue.js +131 -182
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +131 -182
- package/dist/client/vue.mjs.map +1 -1
- package/dist/{events-BgeztGYZ.d.mts → events-CK3N--3g.d.mts} +2 -0
- package/dist/{events-BgeztGYZ.d.ts → events-CK3N--3g.d.ts} +2 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +224 -258
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +224 -258
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-CxogNckF.d.mts → multi-session-client-DzjmT7FX.d.mts} +4 -10
- package/dist/{multi-session-client-cox_WXUj.d.ts → multi-session-client-FAFpUzZ4.d.ts} +4 -10
- package/dist/server/index.d.mts +18 -23
- package/dist/server/index.d.ts +18 -23
- package/dist/server/index.js +133 -85
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +133 -85
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +3 -3
- package/dist/shared/index.d.ts +3 -3
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs.map +1 -1
- package/dist/{types-CLccx9wW.d.mts → types-CW6lghof.d.mts} +6 -0
- package/dist/{types-CLccx9wW.d.ts → types-CW6lghof.d.ts} +6 -0
- package/package.json +1 -1
- package/src/client/core/sse-client.ts +354 -493
- package/src/client/react/index.ts +16 -16
- package/src/client/react/use-mcp-apps.tsx +214 -214
- package/src/client/react/use-mcp.ts +84 -19
- package/src/client/vue/use-mcp.ts +119 -44
- package/src/server/handlers/nextjs-handler.ts +207 -217
- package/src/server/handlers/sse-handler.ts +14 -0
- package/src/server/mcp/oauth-client.ts +48 -46
- package/src/server/storage/types.ts +12 -5
- package/src/shared/events.ts +2 -0
- 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
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
+
}
|