@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
package/dist/client/vue.mjs
CHANGED
|
@@ -5,62 +5,27 @@ import { AppBridge, PostMessageTransport } from '@modelcontextprotocol/ext-apps/
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
7
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
8
|
-
var DEFAULT_REQUEST_TIMEOUT = 6e4;
|
|
9
|
-
var MAX_RECONNECT_ATTEMPTS = 5;
|
|
10
|
-
var BASE_RECONNECT_DELAY = 1e3;
|
|
11
8
|
var SSEClient = class {
|
|
12
9
|
constructor(options) {
|
|
13
10
|
this.options = options;
|
|
14
|
-
__publicField(this, "eventSource", null);
|
|
15
|
-
__publicField(this, "pendingRequests", /* @__PURE__ */ new Map());
|
|
16
11
|
__publicField(this, "resourceCache", /* @__PURE__ */ new Map());
|
|
17
|
-
__publicField(this, "
|
|
18
|
-
__publicField(this, "isManuallyDisconnected", false);
|
|
19
|
-
__publicField(this, "connectionPromise", null);
|
|
20
|
-
__publicField(this, "connectionResolver", null);
|
|
12
|
+
__publicField(this, "connected", false);
|
|
21
13
|
}
|
|
22
|
-
// ============================================
|
|
23
|
-
// Connection Management
|
|
24
|
-
// ============================================
|
|
25
|
-
/**
|
|
26
|
-
* Connect to the SSE endpoint
|
|
27
|
-
*/
|
|
28
14
|
connect() {
|
|
29
|
-
if (this.
|
|
15
|
+
if (this.connected) {
|
|
30
16
|
return;
|
|
31
17
|
}
|
|
32
|
-
this.
|
|
33
|
-
this.options.onStatusChange?.("
|
|
34
|
-
this.
|
|
35
|
-
this.connectionResolver = resolve;
|
|
36
|
-
});
|
|
37
|
-
const url = this.buildUrl();
|
|
38
|
-
this.eventSource = new EventSource(url);
|
|
39
|
-
this.setupEventListeners();
|
|
18
|
+
this.connected = true;
|
|
19
|
+
this.options.onStatusChange?.("connected");
|
|
20
|
+
this.log("RPC mode: post_stream");
|
|
40
21
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Disconnect from the SSE endpoint
|
|
43
|
-
*/
|
|
44
22
|
disconnect() {
|
|
45
|
-
this.
|
|
46
|
-
if (this.eventSource) {
|
|
47
|
-
this.eventSource.close();
|
|
48
|
-
this.eventSource = null;
|
|
49
|
-
}
|
|
50
|
-
this.connectionPromise = null;
|
|
51
|
-
this.connectionResolver = null;
|
|
52
|
-
this.rejectAllPendingRequests(new Error("Connection closed"));
|
|
23
|
+
this.connected = false;
|
|
53
24
|
this.options.onStatusChange?.("disconnected");
|
|
54
25
|
}
|
|
55
|
-
/**
|
|
56
|
-
* Check if connected to the SSE endpoint
|
|
57
|
-
*/
|
|
58
26
|
isConnected() {
|
|
59
|
-
return this.
|
|
27
|
+
return this.connected;
|
|
60
28
|
}
|
|
61
|
-
// ============================================
|
|
62
|
-
// RPC Methods
|
|
63
|
-
// ============================================
|
|
64
29
|
async getSessions() {
|
|
65
30
|
return this.sendRequest("getSessions");
|
|
66
31
|
}
|
|
@@ -96,22 +61,10 @@ var SSEClient = class {
|
|
|
96
61
|
async readResource(sessionId, uri) {
|
|
97
62
|
return this.sendRequest("readResource", { sessionId, uri });
|
|
98
63
|
}
|
|
99
|
-
// ============================================
|
|
100
|
-
// Resource Preloading (for instant UI loading)
|
|
101
|
-
// ============================================
|
|
102
|
-
/**
|
|
103
|
-
* Preload UI resources for tools that have UI metadata.
|
|
104
|
-
* Call this when tools are discovered to enable instant MCP App UI loading.
|
|
105
|
-
*/
|
|
106
64
|
preloadToolUiResources(sessionId, tools) {
|
|
107
65
|
for (const tool of tools) {
|
|
108
66
|
const uri = this.extractUiResourceUri(tool);
|
|
109
|
-
if (!uri) continue;
|
|
110
|
-
if (this.resourceCache.has(uri)) {
|
|
111
|
-
this.log(`Resource already cached: ${uri}`);
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
this.log(`Preloading UI resource for tool "${tool.name}": ${uri}`);
|
|
67
|
+
if (!uri || this.resourceCache.has(uri)) continue;
|
|
115
68
|
const promise = this.sendRequest("readResource", { sessionId, uri }).catch((err) => {
|
|
116
69
|
this.log(`Failed to preload resource ${uri}: ${err.message}`, "warn");
|
|
117
70
|
this.resourceCache.delete(uri);
|
|
@@ -120,43 +73,24 @@ var SSEClient = class {
|
|
|
120
73
|
this.resourceCache.set(uri, promise);
|
|
121
74
|
}
|
|
122
75
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Get a preloaded resource from cache, or fetch if not cached.
|
|
125
|
-
*/
|
|
126
76
|
getOrFetchResource(sessionId, uri) {
|
|
127
77
|
const cached = this.resourceCache.get(uri);
|
|
128
|
-
if (cached)
|
|
129
|
-
this.log(`Cache hit for resource: ${uri}`);
|
|
130
|
-
return cached;
|
|
131
|
-
}
|
|
132
|
-
this.log(`Cache miss, fetching resource: ${uri}`);
|
|
78
|
+
if (cached) return cached;
|
|
133
79
|
const promise = this.sendRequest("readResource", { sessionId, uri });
|
|
134
80
|
this.resourceCache.set(uri, promise);
|
|
135
81
|
return promise;
|
|
136
82
|
}
|
|
137
|
-
/**
|
|
138
|
-
* Check if a resource is already cached
|
|
139
|
-
*/
|
|
140
83
|
hasPreloadedResource(uri) {
|
|
141
84
|
return this.resourceCache.has(uri);
|
|
142
85
|
}
|
|
143
|
-
/**
|
|
144
|
-
* Clear the resource cache
|
|
145
|
-
*/
|
|
146
86
|
clearResourceCache() {
|
|
147
87
|
this.resourceCache.clear();
|
|
148
88
|
}
|
|
149
|
-
// ============================================
|
|
150
|
-
// Private: Request Handling
|
|
151
|
-
// ============================================
|
|
152
|
-
/**
|
|
153
|
-
* Send an RPC request and return the response directly from HTTP.
|
|
154
|
-
* This bypasses SSE latency by returning results in the HTTP response body.
|
|
155
|
-
*/
|
|
156
89
|
async sendRequest(method, params) {
|
|
157
|
-
if (this.
|
|
158
|
-
|
|
90
|
+
if (!this.connected) {
|
|
91
|
+
this.connect();
|
|
159
92
|
}
|
|
93
|
+
this.log(`RPC request via post_stream: ${method}`);
|
|
160
94
|
const request = {
|
|
161
95
|
id: `rpc_${nanoid(10)}`,
|
|
162
96
|
method,
|
|
@@ -170,103 +104,93 @@ var SSEClient = class {
|
|
|
170
104
|
if (!response.ok) {
|
|
171
105
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
172
106
|
}
|
|
173
|
-
const
|
|
174
|
-
|
|
107
|
+
const contentType = (response.headers.get("content-type") || "").toLowerCase();
|
|
108
|
+
if (!contentType.includes("text/event-stream")) {
|
|
109
|
+
const data2 = await response.json();
|
|
110
|
+
return this.parseRpcResponse(data2);
|
|
111
|
+
}
|
|
112
|
+
const data = await this.readRpcResponseFromStream(response);
|
|
113
|
+
return this.parseRpcResponse(data);
|
|
114
|
+
}
|
|
115
|
+
async readRpcResponseFromStream(response) {
|
|
116
|
+
if (!response.body) {
|
|
117
|
+
throw new Error("Streaming response body is missing");
|
|
118
|
+
}
|
|
119
|
+
const reader = response.body.getReader();
|
|
120
|
+
const decoder = new TextDecoder();
|
|
121
|
+
let buffer = "";
|
|
122
|
+
let rpcResponse = null;
|
|
123
|
+
const dispatchBlock = (block) => {
|
|
124
|
+
const lines = block.split("\n");
|
|
125
|
+
let eventName = "message";
|
|
126
|
+
const dataLines = [];
|
|
127
|
+
for (const rawLine of lines) {
|
|
128
|
+
const line = rawLine.replace(/\r$/, "");
|
|
129
|
+
if (!line || line.startsWith(":")) continue;
|
|
130
|
+
if (line.startsWith("event:")) {
|
|
131
|
+
eventName = line.slice("event:".length).trim();
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (line.startsWith("data:")) {
|
|
135
|
+
dataLines.push(line.slice("data:".length).trimStart());
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!dataLines.length) return;
|
|
139
|
+
const payloadText = dataLines.join("\n");
|
|
140
|
+
let payload = payloadText;
|
|
141
|
+
try {
|
|
142
|
+
payload = JSON.parse(payloadText);
|
|
143
|
+
} catch {
|
|
144
|
+
}
|
|
145
|
+
switch (eventName) {
|
|
146
|
+
case "connected":
|
|
147
|
+
this.options.onStatusChange?.("connected");
|
|
148
|
+
break;
|
|
149
|
+
case "connection":
|
|
150
|
+
this.options.onConnectionEvent?.(payload);
|
|
151
|
+
break;
|
|
152
|
+
case "observability":
|
|
153
|
+
this.options.onObservabilityEvent?.(payload);
|
|
154
|
+
break;
|
|
155
|
+
case "rpc-response":
|
|
156
|
+
rpcResponse = payload;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
while (true) {
|
|
161
|
+
const { value, done } = await reader.read();
|
|
162
|
+
if (done) break;
|
|
163
|
+
buffer += decoder.decode(value, { stream: true });
|
|
164
|
+
let separatorMatch = buffer.match(/\r?\n\r?\n/);
|
|
165
|
+
while (separatorMatch && separatorMatch.index !== void 0) {
|
|
166
|
+
const separatorIndex = separatorMatch.index;
|
|
167
|
+
const separatorLength = separatorMatch[0].length;
|
|
168
|
+
const block = buffer.slice(0, separatorIndex);
|
|
169
|
+
buffer = buffer.slice(separatorIndex + separatorLength);
|
|
170
|
+
dispatchBlock(block);
|
|
171
|
+
separatorMatch = buffer.match(/\r?\n\r?\n/);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (buffer.trim()) {
|
|
175
|
+
dispatchBlock(buffer);
|
|
176
|
+
}
|
|
177
|
+
if (!rpcResponse) {
|
|
178
|
+
throw new Error("Missing rpc-response event in streamed RPC result");
|
|
179
|
+
}
|
|
180
|
+
return rpcResponse;
|
|
175
181
|
}
|
|
176
|
-
|
|
177
|
-
* Parse RPC response and handle different response formats
|
|
178
|
-
*/
|
|
179
|
-
parseRpcResponse(data, requestId) {
|
|
182
|
+
parseRpcResponse(data) {
|
|
180
183
|
if ("result" in data) {
|
|
181
184
|
return data.result;
|
|
182
185
|
}
|
|
183
186
|
if ("error" in data && data.error) {
|
|
184
187
|
throw new Error(data.error.message || "Unknown RPC error");
|
|
185
188
|
}
|
|
186
|
-
if ("
|
|
187
|
-
return
|
|
189
|
+
if (data && typeof data === "object" && "id" in data) {
|
|
190
|
+
return void 0;
|
|
188
191
|
}
|
|
189
192
|
throw new Error("Invalid RPC response format");
|
|
190
193
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Wait for RPC response via SSE (legacy fallback)
|
|
193
|
-
*/
|
|
194
|
-
waitForSseResponse(requestId) {
|
|
195
|
-
const timeoutMs = this.options.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT;
|
|
196
|
-
return new Promise((resolve, reject) => {
|
|
197
|
-
const timeoutId = setTimeout(() => {
|
|
198
|
-
this.pendingRequests.delete(requestId);
|
|
199
|
-
reject(new Error(`Request timeout after ${timeoutMs}ms`));
|
|
200
|
-
}, timeoutMs);
|
|
201
|
-
this.pendingRequests.set(requestId, {
|
|
202
|
-
resolve,
|
|
203
|
-
reject,
|
|
204
|
-
timeoutId
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Handle RPC response received via SSE (legacy)
|
|
210
|
-
*/
|
|
211
|
-
handleRpcResponse(response) {
|
|
212
|
-
const pending = this.pendingRequests.get(response.id);
|
|
213
|
-
if (!pending) return;
|
|
214
|
-
clearTimeout(pending.timeoutId);
|
|
215
|
-
this.pendingRequests.delete(response.id);
|
|
216
|
-
if (response.error) {
|
|
217
|
-
pending.reject(new Error(response.error.message));
|
|
218
|
-
} else {
|
|
219
|
-
pending.resolve(response.result);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
// ============================================
|
|
223
|
-
// Private: Event Handling
|
|
224
|
-
// ============================================
|
|
225
|
-
setupEventListeners() {
|
|
226
|
-
if (!this.eventSource) return;
|
|
227
|
-
this.eventSource.addEventListener("open", () => {
|
|
228
|
-
this.log("Connected");
|
|
229
|
-
this.reconnectAttempts = 0;
|
|
230
|
-
this.options.onStatusChange?.("connected");
|
|
231
|
-
});
|
|
232
|
-
this.eventSource.addEventListener("connected", () => {
|
|
233
|
-
this.log("Server ready");
|
|
234
|
-
this.connectionResolver?.();
|
|
235
|
-
this.connectionResolver = null;
|
|
236
|
-
});
|
|
237
|
-
this.eventSource.addEventListener("connection", (e) => {
|
|
238
|
-
const event = JSON.parse(e.data);
|
|
239
|
-
this.options.onConnectionEvent?.(event);
|
|
240
|
-
});
|
|
241
|
-
this.eventSource.addEventListener("observability", (e) => {
|
|
242
|
-
const event = JSON.parse(e.data);
|
|
243
|
-
this.options.onObservabilityEvent?.(event);
|
|
244
|
-
});
|
|
245
|
-
this.eventSource.addEventListener("rpc-response", (e) => {
|
|
246
|
-
const response = JSON.parse(e.data);
|
|
247
|
-
this.handleRpcResponse(response);
|
|
248
|
-
});
|
|
249
|
-
this.eventSource.addEventListener("error", () => {
|
|
250
|
-
this.log("Connection error", "error");
|
|
251
|
-
this.options.onStatusChange?.("error");
|
|
252
|
-
this.attemptReconnect();
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
attemptReconnect() {
|
|
256
|
-
if (this.isManuallyDisconnected || this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
this.reconnectAttempts++;
|
|
260
|
-
const delay = BASE_RECONNECT_DELAY * this.reconnectAttempts;
|
|
261
|
-
this.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
|
|
262
|
-
setTimeout(() => {
|
|
263
|
-
this.disconnect();
|
|
264
|
-
this.connect();
|
|
265
|
-
}, delay);
|
|
266
|
-
}
|
|
267
|
-
// ============================================
|
|
268
|
-
// Private: Utilities
|
|
269
|
-
// ============================================
|
|
270
194
|
buildUrl() {
|
|
271
195
|
const url = new URL(this.options.url, globalThis.location?.origin);
|
|
272
196
|
url.searchParams.set("identity", this.options.identity);
|
|
@@ -277,20 +201,14 @@ var SSEClient = class {
|
|
|
277
201
|
}
|
|
278
202
|
buildHeaders() {
|
|
279
203
|
const headers = {
|
|
280
|
-
"Content-Type": "application/json"
|
|
204
|
+
"Content-Type": "application/json",
|
|
205
|
+
"Accept": "text/event-stream"
|
|
281
206
|
};
|
|
282
207
|
if (this.options.authToken) {
|
|
283
208
|
headers["Authorization"] = `Bearer ${this.options.authToken}`;
|
|
284
209
|
}
|
|
285
210
|
return headers;
|
|
286
211
|
}
|
|
287
|
-
rejectAllPendingRequests(error) {
|
|
288
|
-
for (const [, pending] of this.pendingRequests) {
|
|
289
|
-
clearTimeout(pending.timeoutId);
|
|
290
|
-
pending.reject(error);
|
|
291
|
-
}
|
|
292
|
-
this.pendingRequests.clear();
|
|
293
|
-
}
|
|
294
212
|
extractUiResourceUri(tool) {
|
|
295
213
|
const meta = tool._meta?.ui;
|
|
296
214
|
if (!meta || typeof meta !== "object") return void 0;
|
|
@@ -341,23 +259,35 @@ function useMcp(options) {
|
|
|
341
259
|
} = options;
|
|
342
260
|
const clientRef = shallowRef(null);
|
|
343
261
|
const isMountedRef = ref(true);
|
|
262
|
+
const suppressAuthRedirectSessions = ref(/* @__PURE__ */ new Set());
|
|
344
263
|
const connections = ref([]);
|
|
345
264
|
const status = ref("disconnected");
|
|
346
265
|
const isInitializing = ref(false);
|
|
347
266
|
const updateConnectionsFromEvent = (event) => {
|
|
348
267
|
if (!isMountedRef.value) return;
|
|
268
|
+
const isTransientReconnectState = (state) => state === "INITIALIZING" || state === "VALIDATING" || state === "RECONNECTING" || state === "CONNECTING" || state === "CONNECTED" || state === "DISCOVERING";
|
|
349
269
|
switch (event.type) {
|
|
350
270
|
case "state_changed": {
|
|
351
271
|
const existing = connections.value.find((c) => c.sessionId === event.sessionId);
|
|
352
272
|
if (existing) {
|
|
273
|
+
const nextState = existing.state === "READY" && isTransientReconnectState(event.state) ? existing.state : event.state;
|
|
353
274
|
const index = connections.value.indexOf(existing);
|
|
354
|
-
connections.value[index] = {
|
|
275
|
+
connections.value[index] = {
|
|
276
|
+
...existing,
|
|
277
|
+
state: nextState,
|
|
278
|
+
// update createdAt if present in event, otherwise keep existing
|
|
279
|
+
createdAt: event.createdAt ? new Date(event.createdAt) : existing.createdAt
|
|
280
|
+
};
|
|
355
281
|
} else {
|
|
282
|
+
if (event.state === "DISCONNECTED") {
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
356
285
|
connections.value = [...connections.value, {
|
|
357
286
|
sessionId: event.sessionId,
|
|
358
287
|
serverId: event.serverId,
|
|
359
288
|
serverName: event.serverName,
|
|
360
289
|
state: event.state,
|
|
290
|
+
createdAt: event.createdAt ? new Date(event.createdAt) : void 0,
|
|
361
291
|
tools: []
|
|
362
292
|
}];
|
|
363
293
|
}
|
|
@@ -373,15 +303,17 @@ function useMcp(options) {
|
|
|
373
303
|
case "auth_required": {
|
|
374
304
|
if (event.authUrl) {
|
|
375
305
|
onLog?.("info", `OAuth required - redirecting to ${event.authUrl}`, { authUrl: event.authUrl });
|
|
376
|
-
if (
|
|
377
|
-
onRedirect
|
|
378
|
-
|
|
379
|
-
window
|
|
306
|
+
if (!suppressAuthRedirectSessions.value.has(event.sessionId)) {
|
|
307
|
+
if (onRedirect) {
|
|
308
|
+
onRedirect(event.authUrl);
|
|
309
|
+
} else if (typeof window !== "undefined") {
|
|
310
|
+
window.location.href = event.authUrl;
|
|
311
|
+
}
|
|
380
312
|
}
|
|
381
313
|
}
|
|
382
314
|
const index = connections.value.findIndex((c) => c.sessionId === event.sessionId);
|
|
383
315
|
if (index !== -1) {
|
|
384
|
-
connections.value[index] = { ...connections.value[index], state: "AUTHENTICATING" };
|
|
316
|
+
connections.value[index] = { ...connections.value[index], state: "AUTHENTICATING", authUrl: event.authUrl };
|
|
385
317
|
}
|
|
386
318
|
break;
|
|
387
319
|
}
|
|
@@ -411,7 +343,8 @@ function useMcp(options) {
|
|
|
411
343
|
serverName: s.serverName ?? "Unknown Server",
|
|
412
344
|
serverUrl: s.serverUrl,
|
|
413
345
|
transport: s.transport,
|
|
414
|
-
state: "VALIDATING",
|
|
346
|
+
state: s.active === false ? "AUTHENTICATING" : "VALIDATING",
|
|
347
|
+
createdAt: new Date(s.createdAt),
|
|
415
348
|
tools: []
|
|
416
349
|
}));
|
|
417
350
|
}
|
|
@@ -419,9 +352,15 @@ function useMcp(options) {
|
|
|
419
352
|
sessions.map(async (session) => {
|
|
420
353
|
if (clientRef.value) {
|
|
421
354
|
try {
|
|
355
|
+
if (session.active === false) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
suppressAuthRedirectSessions.value.add(session.sessionId);
|
|
422
359
|
await clientRef.value.restoreSession(session.sessionId);
|
|
423
360
|
} catch (error) {
|
|
424
361
|
console.error(`[useMcp] Failed to validate session ${session.sessionId}:`, error);
|
|
362
|
+
} finally {
|
|
363
|
+
suppressAuthRedirectSessions.value.delete(session.sessionId);
|
|
425
364
|
}
|
|
426
365
|
}
|
|
427
366
|
})
|
|
@@ -454,7 +393,8 @@ function useMcp(options) {
|
|
|
454
393
|
if (isMountedRef.value) {
|
|
455
394
|
status.value = newStatus;
|
|
456
395
|
}
|
|
457
|
-
}
|
|
396
|
+
},
|
|
397
|
+
debug: options.debug
|
|
458
398
|
};
|
|
459
399
|
const client = new SSEClient(clientOptions);
|
|
460
400
|
clientRef.value = client;
|
|
@@ -504,6 +444,13 @@ function useMcp(options) {
|
|
|
504
444
|
}
|
|
505
445
|
return await clientRef.value.finishAuth(sessionId, code);
|
|
506
446
|
};
|
|
447
|
+
const resumeAuth = async (sessionId) => {
|
|
448
|
+
if (!clientRef.value) {
|
|
449
|
+
throw new Error("SSE client not initialized");
|
|
450
|
+
}
|
|
451
|
+
suppressAuthRedirectSessions.value.delete(sessionId);
|
|
452
|
+
await clientRef.value.restoreSession(sessionId);
|
|
453
|
+
};
|
|
507
454
|
const callTool = async (sessionId, toolName, toolArgs) => {
|
|
508
455
|
if (!clientRef.value) {
|
|
509
456
|
throw new Error("SSE client not initialized");
|
|
@@ -565,12 +512,14 @@ function useMcp(options) {
|
|
|
565
512
|
connectSSE,
|
|
566
513
|
disconnectSSE,
|
|
567
514
|
finishAuth,
|
|
515
|
+
resumeAuth,
|
|
568
516
|
callTool,
|
|
569
517
|
listTools,
|
|
570
518
|
listPrompts,
|
|
571
519
|
getPrompt,
|
|
572
520
|
listResources,
|
|
573
|
-
readResource
|
|
521
|
+
readResource,
|
|
522
|
+
sseClient: clientRef.value
|
|
574
523
|
};
|
|
575
524
|
}
|
|
576
525
|
var HOST_INFO = { name: "mcp-ts-host", version: "1.0.0" };
|