@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.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,19 +261,22 @@ 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
277
|
connections.value[index] = {
|
|
357
278
|
...existing,
|
|
358
|
-
state:
|
|
279
|
+
state: nextState,
|
|
359
280
|
// update createdAt if present in event, otherwise keep existing
|
|
360
281
|
createdAt: event.createdAt ? new Date(event.createdAt) : existing.createdAt
|
|
361
282
|
};
|
|
@@ -384,15 +305,17 @@ function useMcp(options) {
|
|
|
384
305
|
case "auth_required": {
|
|
385
306
|
if (event.authUrl) {
|
|
386
307
|
onLog?.("info", `OAuth required - redirecting to ${event.authUrl}`, { authUrl: event.authUrl });
|
|
387
|
-
if (
|
|
388
|
-
onRedirect
|
|
389
|
-
|
|
390
|
-
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
|
+
}
|
|
391
314
|
}
|
|
392
315
|
}
|
|
393
316
|
const index = connections.value.findIndex((c) => c.sessionId === event.sessionId);
|
|
394
317
|
if (index !== -1) {
|
|
395
|
-
connections.value[index] = { ...connections.value[index], state: "AUTHENTICATING" };
|
|
318
|
+
connections.value[index] = { ...connections.value[index], state: "AUTHENTICATING", authUrl: event.authUrl };
|
|
396
319
|
}
|
|
397
320
|
break;
|
|
398
321
|
}
|
|
@@ -422,7 +345,7 @@ function useMcp(options) {
|
|
|
422
345
|
serverName: s.serverName ?? "Unknown Server",
|
|
423
346
|
serverUrl: s.serverUrl,
|
|
424
347
|
transport: s.transport,
|
|
425
|
-
state: "VALIDATING",
|
|
348
|
+
state: s.active === false ? "AUTHENTICATING" : "VALIDATING",
|
|
426
349
|
createdAt: new Date(s.createdAt),
|
|
427
350
|
tools: []
|
|
428
351
|
}));
|
|
@@ -431,9 +354,15 @@ function useMcp(options) {
|
|
|
431
354
|
sessions.map(async (session) => {
|
|
432
355
|
if (clientRef.value) {
|
|
433
356
|
try {
|
|
357
|
+
if (session.active === false) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
suppressAuthRedirectSessions.value.add(session.sessionId);
|
|
434
361
|
await clientRef.value.restoreSession(session.sessionId);
|
|
435
362
|
} catch (error) {
|
|
436
363
|
console.error(`[useMcp] Failed to validate session ${session.sessionId}:`, error);
|
|
364
|
+
} finally {
|
|
365
|
+
suppressAuthRedirectSessions.value.delete(session.sessionId);
|
|
437
366
|
}
|
|
438
367
|
}
|
|
439
368
|
})
|
|
@@ -466,7 +395,8 @@ function useMcp(options) {
|
|
|
466
395
|
if (isMountedRef.value) {
|
|
467
396
|
status.value = newStatus;
|
|
468
397
|
}
|
|
469
|
-
}
|
|
398
|
+
},
|
|
399
|
+
debug: options.debug
|
|
470
400
|
};
|
|
471
401
|
const client = new SSEClient(clientOptions);
|
|
472
402
|
clientRef.value = client;
|
|
@@ -516,6 +446,13 @@ function useMcp(options) {
|
|
|
516
446
|
}
|
|
517
447
|
return await clientRef.value.finishAuth(sessionId, code);
|
|
518
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
|
+
};
|
|
519
456
|
const callTool = async (sessionId, toolName, toolArgs) => {
|
|
520
457
|
if (!clientRef.value) {
|
|
521
458
|
throw new Error("SSE client not initialized");
|
|
@@ -577,12 +514,14 @@ function useMcp(options) {
|
|
|
577
514
|
connectSSE,
|
|
578
515
|
disconnectSSE,
|
|
579
516
|
finishAuth,
|
|
517
|
+
resumeAuth,
|
|
580
518
|
callTool,
|
|
581
519
|
listTools,
|
|
582
520
|
listPrompts,
|
|
583
521
|
getPrompt,
|
|
584
522
|
listResources,
|
|
585
|
-
readResource
|
|
523
|
+
readResource,
|
|
524
|
+
sseClient: clientRef.value
|
|
586
525
|
};
|
|
587
526
|
}
|
|
588
527
|
var HOST_INFO = { name: "mcp-ts-host", version: "1.0.0" };
|