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