@mcp-ts/sdk 1.3.2 → 1.3.4
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 +400 -406
- package/dist/adapters/agui-adapter.d.mts +1 -1
- package/dist/adapters/agui-adapter.d.ts +1 -1
- package/dist/adapters/agui-middleware.d.mts +1 -1
- package/dist/adapters/agui-middleware.d.ts +1 -1
- package/dist/adapters/ai-adapter.d.mts +1 -1
- package/dist/adapters/ai-adapter.d.ts +1 -1
- package/dist/adapters/langchain-adapter.d.mts +1 -1
- package/dist/adapters/langchain-adapter.d.ts +1 -1
- package/dist/adapters/mastra-adapter.d.mts +1 -1
- package/dist/adapters/mastra-adapter.d.ts +1 -1
- package/dist/client/index.d.mts +8 -64
- package/dist/client/index.d.ts +8 -64
- 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 +12 -2
- package/dist/client/react.d.ts +12 -2
- package/dist/client/react.js +119 -182
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +119 -182
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +24 -4
- package/dist/client/vue.d.ts +24 -4
- package/dist/client/vue.js +121 -182
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +121 -182
- package/dist/client/vue.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +215 -250
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +215 -250
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-B1DBx5yR.d.mts → multi-session-client-DzjmT7FX.d.mts} +1 -0
- package/dist/{multi-session-client-DyFzyJUx.d.ts → multi-session-client-FAFpUzZ4.d.ts} +1 -0
- package/dist/server/index.d.mts +16 -21
- package/dist/server/index.d.ts +16 -21
- package/dist/server/index.js +124 -77
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +124 -77
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +2 -2
- package/dist/shared/index.d.ts +2 -2
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs.map +1 -1
- package/dist/{types-PjM1W07s.d.mts → types-CW6lghof.d.mts} +5 -0
- package/dist/{types-PjM1W07s.d.ts → types-CW6lghof.d.ts} +5 -0
- package/package.json +1 -1
- package/src/client/core/sse-client.ts +354 -493
- package/src/client/react/use-mcp.ts +75 -23
- package/src/client/vue/use-mcp.ts +111 -48
- package/src/server/handlers/nextjs-handler.ts +207 -217
- package/src/server/handlers/sse-handler.ts +10 -0
- package/src/server/mcp/oauth-client.ts +41 -32
- package/src/server/storage/types.ts +12 -5
- package/src/shared/types.ts +5 -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,19 +259,22 @@ 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
275
|
connections.value[index] = {
|
|
355
276
|
...existing,
|
|
356
|
-
state:
|
|
277
|
+
state: nextState,
|
|
357
278
|
// update createdAt if present in event, otherwise keep existing
|
|
358
279
|
createdAt: event.createdAt ? new Date(event.createdAt) : existing.createdAt
|
|
359
280
|
};
|
|
@@ -382,15 +303,17 @@ function useMcp(options) {
|
|
|
382
303
|
case "auth_required": {
|
|
383
304
|
if (event.authUrl) {
|
|
384
305
|
onLog?.("info", `OAuth required - redirecting to ${event.authUrl}`, { authUrl: event.authUrl });
|
|
385
|
-
if (
|
|
386
|
-
onRedirect
|
|
387
|
-
|
|
388
|
-
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
|
+
}
|
|
389
312
|
}
|
|
390
313
|
}
|
|
391
314
|
const index = connections.value.findIndex((c) => c.sessionId === event.sessionId);
|
|
392
315
|
if (index !== -1) {
|
|
393
|
-
connections.value[index] = { ...connections.value[index], state: "AUTHENTICATING" };
|
|
316
|
+
connections.value[index] = { ...connections.value[index], state: "AUTHENTICATING", authUrl: event.authUrl };
|
|
394
317
|
}
|
|
395
318
|
break;
|
|
396
319
|
}
|
|
@@ -420,7 +343,7 @@ function useMcp(options) {
|
|
|
420
343
|
serverName: s.serverName ?? "Unknown Server",
|
|
421
344
|
serverUrl: s.serverUrl,
|
|
422
345
|
transport: s.transport,
|
|
423
|
-
state: "VALIDATING",
|
|
346
|
+
state: s.active === false ? "AUTHENTICATING" : "VALIDATING",
|
|
424
347
|
createdAt: new Date(s.createdAt),
|
|
425
348
|
tools: []
|
|
426
349
|
}));
|
|
@@ -429,9 +352,15 @@ function useMcp(options) {
|
|
|
429
352
|
sessions.map(async (session) => {
|
|
430
353
|
if (clientRef.value) {
|
|
431
354
|
try {
|
|
355
|
+
if (session.active === false) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
suppressAuthRedirectSessions.value.add(session.sessionId);
|
|
432
359
|
await clientRef.value.restoreSession(session.sessionId);
|
|
433
360
|
} catch (error) {
|
|
434
361
|
console.error(`[useMcp] Failed to validate session ${session.sessionId}:`, error);
|
|
362
|
+
} finally {
|
|
363
|
+
suppressAuthRedirectSessions.value.delete(session.sessionId);
|
|
435
364
|
}
|
|
436
365
|
}
|
|
437
366
|
})
|
|
@@ -464,7 +393,8 @@ function useMcp(options) {
|
|
|
464
393
|
if (isMountedRef.value) {
|
|
465
394
|
status.value = newStatus;
|
|
466
395
|
}
|
|
467
|
-
}
|
|
396
|
+
},
|
|
397
|
+
debug: options.debug
|
|
468
398
|
};
|
|
469
399
|
const client = new SSEClient(clientOptions);
|
|
470
400
|
clientRef.value = client;
|
|
@@ -514,6 +444,13 @@ function useMcp(options) {
|
|
|
514
444
|
}
|
|
515
445
|
return await clientRef.value.finishAuth(sessionId, code);
|
|
516
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
|
+
};
|
|
517
454
|
const callTool = async (sessionId, toolName, toolArgs) => {
|
|
518
455
|
if (!clientRef.value) {
|
|
519
456
|
throw new Error("SSE client not initialized");
|
|
@@ -575,12 +512,14 @@ function useMcp(options) {
|
|
|
575
512
|
connectSSE,
|
|
576
513
|
disconnectSSE,
|
|
577
514
|
finishAuth,
|
|
515
|
+
resumeAuth,
|
|
578
516
|
callTool,
|
|
579
517
|
listTools,
|
|
580
518
|
listPrompts,
|
|
581
519
|
getPrompt,
|
|
582
520
|
listResources,
|
|
583
|
-
readResource
|
|
521
|
+
readResource,
|
|
522
|
+
sseClient: clientRef.value
|
|
584
523
|
};
|
|
585
524
|
}
|
|
586
525
|
var HOST_INFO = { name: "mcp-ts-host", version: "1.0.0" };
|