@mcp-ts/sdk 1.3.3 → 1.3.5
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 +404 -405
- 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 +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +14 -5
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +14 -5
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.js +15 -6
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +15 -6
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.js +15 -6
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +15 -6
- package/dist/client/vue.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +201 -158
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +201 -158
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-FAFpUzZ4.d.ts → multi-session-client-BYLarghq.d.ts} +29 -19
- package/dist/{multi-session-client-DzjmT7FX.d.mts → multi-session-client-CzhMkE0k.d.mts} +29 -19
- package/dist/server/index.d.mts +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +193 -151
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +193 -151
- 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 +2 -2
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +2 -2
- package/dist/shared/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client/core/sse-client.ts +371 -354
- package/src/client/react/use-mcp.ts +31 -31
- package/src/client/vue/use-mcp.ts +77 -77
- package/src/server/handlers/nextjs-handler.ts +194 -197
- package/src/server/handlers/sse-handler.ts +62 -111
- package/src/server/mcp/oauth-client.ts +67 -79
- package/src/server/mcp/storage-oauth-provider.ts +71 -38
- package/src/server/storage/index.ts +15 -13
- package/src/server/storage/redis-backend.ts +93 -23
- package/src/server/storage/types.ts +12 -12
- package/src/shared/constants.ts +2 -2
- package/src/shared/event-routing.ts +28 -0
package/dist/index.js
CHANGED
|
@@ -142,10 +142,10 @@ var REDIS_KEY_PREFIX = "mcp:session:";
|
|
|
142
142
|
var TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1e3;
|
|
143
143
|
var DEFAULT_CLIENT_NAME = "MCP Assistant";
|
|
144
144
|
var DEFAULT_CLIENT_URI = "https://mcp-assistant.in";
|
|
145
|
-
var DEFAULT_LOGO_URI = "https://mcp-assistant.in/logo.
|
|
145
|
+
var DEFAULT_LOGO_URI = "https://mcp-assistant.in/logo.svg";
|
|
146
146
|
var DEFAULT_POLICY_URI = "https://mcp-assistant.in/privacy";
|
|
147
147
|
var SOFTWARE_ID = "@mcp-ts";
|
|
148
|
-
var SOFTWARE_VERSION = "1.
|
|
148
|
+
var SOFTWARE_VERSION = "1.3.4";
|
|
149
149
|
var MCP_CLIENT_NAME = "mcp-ts-oauth-client";
|
|
150
150
|
var MCP_CLIENT_VERSION = "2.0";
|
|
151
151
|
|
|
@@ -163,6 +163,8 @@ var RedisStorageBackend = class {
|
|
|
163
163
|
this.redis = redis2;
|
|
164
164
|
__publicField(this, "DEFAULT_TTL", SESSION_TTL_SECONDS);
|
|
165
165
|
__publicField(this, "KEY_PREFIX", "mcp:session:");
|
|
166
|
+
__publicField(this, "IDENTITY_KEY_PREFIX", "mcp:identity:");
|
|
167
|
+
__publicField(this, "IDENTITY_KEY_SUFFIX", ":sessions");
|
|
166
168
|
}
|
|
167
169
|
/**
|
|
168
170
|
* Generates Redis key for a specific session
|
|
@@ -176,7 +178,34 @@ var RedisStorageBackend = class {
|
|
|
176
178
|
* @private
|
|
177
179
|
*/
|
|
178
180
|
getIdentityKey(identity) {
|
|
179
|
-
return
|
|
181
|
+
return `${this.IDENTITY_KEY_PREFIX}${identity}${this.IDENTITY_KEY_SUFFIX}`;
|
|
182
|
+
}
|
|
183
|
+
parseIdentityFromKey(identityKey) {
|
|
184
|
+
return identityKey.slice(
|
|
185
|
+
this.IDENTITY_KEY_PREFIX.length,
|
|
186
|
+
identityKey.length - this.IDENTITY_KEY_SUFFIX.length
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
async scanKeys(pattern) {
|
|
190
|
+
const redis2 = this.redis;
|
|
191
|
+
if (typeof redis2.scan !== "function") {
|
|
192
|
+
return await this.redis.keys(pattern);
|
|
193
|
+
}
|
|
194
|
+
const keys = /* @__PURE__ */ new Set();
|
|
195
|
+
let cursor = "0";
|
|
196
|
+
try {
|
|
197
|
+
do {
|
|
198
|
+
const [nextCursor, batch] = await redis2.scan(cursor, "MATCH", pattern, "COUNT", 100);
|
|
199
|
+
cursor = nextCursor;
|
|
200
|
+
for (const key of batch) {
|
|
201
|
+
keys.add(key);
|
|
202
|
+
}
|
|
203
|
+
} while (cursor !== "0");
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.warn("[RedisStorage] SCAN failed, falling back to KEYS:", error);
|
|
206
|
+
return await this.redis.keys(pattern);
|
|
207
|
+
}
|
|
208
|
+
return Array.from(keys);
|
|
180
209
|
}
|
|
181
210
|
generateSessionId() {
|
|
182
211
|
return firstChar() + rest();
|
|
@@ -244,17 +273,13 @@ var RedisStorageBackend = class {
|
|
|
244
273
|
}
|
|
245
274
|
}
|
|
246
275
|
async getIdentityMcpSessions(identity) {
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
return await this.redis.smembers(identityKey);
|
|
250
|
-
} catch (error) {
|
|
251
|
-
console.error(`[RedisStorage] Failed to get sessions for ${identity}:`, error);
|
|
252
|
-
return [];
|
|
253
|
-
}
|
|
276
|
+
const sessions = await this.getIdentitySessionsData(identity);
|
|
277
|
+
return sessions.map((session) => session.sessionId);
|
|
254
278
|
}
|
|
255
279
|
async getIdentitySessionsData(identity) {
|
|
256
280
|
try {
|
|
257
|
-
const
|
|
281
|
+
const identityKey = this.getIdentityKey(identity);
|
|
282
|
+
const sessionIds = await this.redis.smembers(identityKey);
|
|
258
283
|
if (sessionIds.length === 0) return [];
|
|
259
284
|
const results = await Promise.all(
|
|
260
285
|
sessionIds.map(async (sessionId) => {
|
|
@@ -262,6 +287,10 @@ var RedisStorageBackend = class {
|
|
|
262
287
|
return data ? JSON.parse(data) : null;
|
|
263
288
|
})
|
|
264
289
|
);
|
|
290
|
+
const staleSessionIds = sessionIds.filter((_, index) => results[index] === null);
|
|
291
|
+
if (staleSessionIds.length > 0) {
|
|
292
|
+
await this.redis.srem(identityKey, ...staleSessionIds);
|
|
293
|
+
}
|
|
265
294
|
return results.filter((session) => session !== null);
|
|
266
295
|
} catch (error) {
|
|
267
296
|
console.error(`[RedisStorage] Failed to get session data for ${identity}:`, error);
|
|
@@ -280,9 +309,22 @@ var RedisStorageBackend = class {
|
|
|
280
309
|
}
|
|
281
310
|
async getAllSessionIds() {
|
|
282
311
|
try {
|
|
283
|
-
const
|
|
284
|
-
const
|
|
285
|
-
|
|
312
|
+
const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
|
|
313
|
+
const sessions = await Promise.all(
|
|
314
|
+
keys.map(async (key) => {
|
|
315
|
+
const data = await this.redis.get(key);
|
|
316
|
+
if (!data) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
return JSON.parse(data).sessionId;
|
|
321
|
+
} catch (error) {
|
|
322
|
+
console.error("[RedisStorage] Failed to parse session while listing all session IDs:", error);
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
);
|
|
327
|
+
return sessions.filter((sessionId) => sessionId !== null);
|
|
286
328
|
} catch (error) {
|
|
287
329
|
console.error("[RedisStorage] Failed to get all sessions:", error);
|
|
288
330
|
return [];
|
|
@@ -290,10 +332,11 @@ var RedisStorageBackend = class {
|
|
|
290
332
|
}
|
|
291
333
|
async clearAll() {
|
|
292
334
|
try {
|
|
293
|
-
const
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
335
|
+
const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
|
|
336
|
+
const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
|
|
337
|
+
const allKeys = [...keys, ...identityKeys];
|
|
338
|
+
if (allKeys.length > 0) {
|
|
339
|
+
await this.redis.del(...allKeys);
|
|
297
340
|
}
|
|
298
341
|
} catch (error) {
|
|
299
342
|
console.error("[RedisStorage] Failed to clear sessions:", error);
|
|
@@ -301,12 +344,24 @@ var RedisStorageBackend = class {
|
|
|
301
344
|
}
|
|
302
345
|
async cleanupExpiredSessions() {
|
|
303
346
|
try {
|
|
304
|
-
const
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
if (
|
|
309
|
-
await this.redis.del(
|
|
347
|
+
const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
|
|
348
|
+
for (const identityKey of identityKeys) {
|
|
349
|
+
const identity = this.parseIdentityFromKey(identityKey);
|
|
350
|
+
const sessionIds = await this.redis.smembers(identityKey);
|
|
351
|
+
if (sessionIds.length === 0) {
|
|
352
|
+
await this.redis.del(identityKey);
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
const existenceChecks = await Promise.all(
|
|
356
|
+
sessionIds.map((sessionId) => this.redis.exists(this.getSessionKey(identity, sessionId)))
|
|
357
|
+
);
|
|
358
|
+
const staleSessionIds = sessionIds.filter((_, index) => existenceChecks[index] === 0);
|
|
359
|
+
if (staleSessionIds.length > 0) {
|
|
360
|
+
await this.redis.srem(identityKey, ...staleSessionIds);
|
|
361
|
+
}
|
|
362
|
+
const remainingCount = await this.redis.scard(identityKey);
|
|
363
|
+
if (remainingCount === 0) {
|
|
364
|
+
await this.redis.del(identityKey);
|
|
310
365
|
}
|
|
311
366
|
}
|
|
312
367
|
} catch (error) {
|
|
@@ -677,6 +732,12 @@ var SqliteStorage = class {
|
|
|
677
732
|
// src/server/storage/index.ts
|
|
678
733
|
var storageInstance = null;
|
|
679
734
|
var storagePromise = null;
|
|
735
|
+
async function initializeStorage(store) {
|
|
736
|
+
if (typeof store.init === "function") {
|
|
737
|
+
await store.init();
|
|
738
|
+
}
|
|
739
|
+
return store;
|
|
740
|
+
}
|
|
680
741
|
async function createStorage() {
|
|
681
742
|
const type = process.env.MCP_TS_STORAGE_TYPE?.toLowerCase();
|
|
682
743
|
if (type === "redis") {
|
|
@@ -700,16 +761,12 @@ async function createStorage() {
|
|
|
700
761
|
console.warn('[Storage] MCP_TS_STORAGE_TYPE is "file" but MCP_TS_STORAGE_FILE is missing');
|
|
701
762
|
}
|
|
702
763
|
console.log(`[Storage] Using File storage (${filePath}) (Explicit)`);
|
|
703
|
-
|
|
704
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
705
|
-
return store;
|
|
764
|
+
return await initializeStorage(new FileStorageBackend({ path: filePath }));
|
|
706
765
|
}
|
|
707
766
|
if (type === "sqlite") {
|
|
708
767
|
const dbPath = process.env.MCP_TS_STORAGE_SQLITE_PATH;
|
|
709
768
|
console.log(`[Storage] Using SQLite storage (${dbPath || "default"}) (Explicit)`);
|
|
710
|
-
|
|
711
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
712
|
-
return store;
|
|
769
|
+
return await initializeStorage(new SqliteStorage({ path: dbPath }));
|
|
713
770
|
}
|
|
714
771
|
if (type === "memory") {
|
|
715
772
|
console.log("[Storage] Using In-Memory storage (Explicit)");
|
|
@@ -729,15 +786,11 @@ async function createStorage() {
|
|
|
729
786
|
}
|
|
730
787
|
if (process.env.MCP_TS_STORAGE_FILE) {
|
|
731
788
|
console.log(`[Storage] Auto-detected MCP_TS_STORAGE_FILE. Using File storage (${process.env.MCP_TS_STORAGE_FILE}).`);
|
|
732
|
-
|
|
733
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
734
|
-
return store;
|
|
789
|
+
return await initializeStorage(new FileStorageBackend({ path: process.env.MCP_TS_STORAGE_FILE }));
|
|
735
790
|
}
|
|
736
791
|
if (process.env.MCP_TS_STORAGE_SQLITE_PATH) {
|
|
737
792
|
console.log(`[Storage] Auto-detected MCP_TS_STORAGE_SQLITE_PATH. Using SQLite storage (${process.env.MCP_TS_STORAGE_SQLITE_PATH}).`);
|
|
738
|
-
|
|
739
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
740
|
-
return store;
|
|
793
|
+
return await initializeStorage(new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH }));
|
|
741
794
|
}
|
|
742
795
|
console.log("[Storage] No storage configured. Using In-Memory storage (Default).");
|
|
743
796
|
return new MemoryStorageBackend();
|
|
@@ -747,7 +800,10 @@ async function getStorage() {
|
|
|
747
800
|
return storageInstance;
|
|
748
801
|
}
|
|
749
802
|
if (!storagePromise) {
|
|
750
|
-
storagePromise = createStorage()
|
|
803
|
+
storagePromise = createStorage().catch((error) => {
|
|
804
|
+
storagePromise = null;
|
|
805
|
+
throw error;
|
|
806
|
+
});
|
|
751
807
|
}
|
|
752
808
|
storageInstance = await storagePromise;
|
|
753
809
|
return storageInstance;
|
|
@@ -768,43 +824,49 @@ var storage = new Proxy({}, {
|
|
|
768
824
|
// src/server/mcp/storage-oauth-provider.ts
|
|
769
825
|
var StorageOAuthClientProvider = class {
|
|
770
826
|
/**
|
|
771
|
-
* Creates a new
|
|
772
|
-
* @param
|
|
773
|
-
* @param serverId - Server identifier (for tracking which server this OAuth session belongs to)
|
|
774
|
-
* @param sessionId - Session identifier (used as OAuth state)
|
|
775
|
-
* @param clientName - OAuth client name
|
|
776
|
-
* @param baseRedirectUrl - OAuth callback URL
|
|
777
|
-
* @param onRedirect - Optional callback when redirect to authorization is needed
|
|
827
|
+
* Creates a new storage-backed OAuth provider
|
|
828
|
+
* @param options - Provider configuration
|
|
778
829
|
*/
|
|
779
|
-
constructor(
|
|
780
|
-
this
|
|
781
|
-
this
|
|
782
|
-
this
|
|
783
|
-
this
|
|
784
|
-
this
|
|
830
|
+
constructor(options) {
|
|
831
|
+
__publicField(this, "identity");
|
|
832
|
+
__publicField(this, "serverId");
|
|
833
|
+
__publicField(this, "sessionId");
|
|
834
|
+
__publicField(this, "redirectUrl");
|
|
835
|
+
__publicField(this, "clientName");
|
|
836
|
+
__publicField(this, "clientUri");
|
|
837
|
+
__publicField(this, "logoUri");
|
|
838
|
+
__publicField(this, "policyUri");
|
|
839
|
+
__publicField(this, "clientSecret");
|
|
785
840
|
__publicField(this, "_authUrl");
|
|
786
841
|
__publicField(this, "_clientId");
|
|
787
842
|
__publicField(this, "onRedirectCallback");
|
|
788
843
|
__publicField(this, "tokenExpiresAt");
|
|
789
|
-
this.
|
|
844
|
+
this.identity = options.identity;
|
|
845
|
+
this.serverId = options.serverId;
|
|
846
|
+
this.sessionId = options.sessionId;
|
|
847
|
+
this.redirectUrl = options.redirectUrl;
|
|
848
|
+
this.clientName = options.clientName;
|
|
849
|
+
this.clientUri = options.clientUri;
|
|
850
|
+
this.logoUri = options.logoUri;
|
|
851
|
+
this.policyUri = options.policyUri;
|
|
852
|
+
this._clientId = options.clientId;
|
|
853
|
+
this.clientSecret = options.clientSecret;
|
|
854
|
+
this.onRedirectCallback = options.onRedirect;
|
|
790
855
|
}
|
|
791
856
|
get clientMetadata() {
|
|
792
857
|
return {
|
|
793
|
-
client_name: this.clientName,
|
|
794
|
-
client_uri: this.clientUri,
|
|
858
|
+
client_name: this.clientName || DEFAULT_CLIENT_NAME,
|
|
859
|
+
client_uri: this.clientUri || DEFAULT_CLIENT_URI,
|
|
860
|
+
logo_uri: this.logoUri || DEFAULT_LOGO_URI,
|
|
861
|
+
policy_uri: this.policyUri || DEFAULT_POLICY_URI,
|
|
795
862
|
grant_types: ["authorization_code", "refresh_token"],
|
|
796
863
|
redirect_uris: [this.redirectUrl],
|
|
797
864
|
response_types: ["code"],
|
|
798
|
-
token_endpoint_auth_method: "none",
|
|
799
|
-
|
|
865
|
+
token_endpoint_auth_method: this.clientSecret ? "client_secret_basic" : "none",
|
|
866
|
+
software_id: SOFTWARE_ID,
|
|
867
|
+
software_version: SOFTWARE_VERSION
|
|
800
868
|
};
|
|
801
869
|
}
|
|
802
|
-
get clientUri() {
|
|
803
|
-
return new URL(this.redirectUrl).origin;
|
|
804
|
-
}
|
|
805
|
-
get redirectUrl() {
|
|
806
|
-
return this.baseRedirectUrl;
|
|
807
|
-
}
|
|
808
870
|
get clientId() {
|
|
809
871
|
return this._clientId;
|
|
810
872
|
}
|
|
@@ -839,7 +901,16 @@ var StorageOAuthClientProvider = class {
|
|
|
839
901
|
if (data.clientId && !this._clientId) {
|
|
840
902
|
this._clientId = data.clientId;
|
|
841
903
|
}
|
|
842
|
-
|
|
904
|
+
if (data.clientInformation) {
|
|
905
|
+
return data.clientInformation;
|
|
906
|
+
}
|
|
907
|
+
if (!this._clientId) {
|
|
908
|
+
return void 0;
|
|
909
|
+
}
|
|
910
|
+
return {
|
|
911
|
+
client_id: this._clientId,
|
|
912
|
+
...this.clientSecret ? { client_secret: this.clientSecret } : {}
|
|
913
|
+
};
|
|
843
914
|
}
|
|
844
915
|
/**
|
|
845
916
|
* Stores OAuth client information
|
|
@@ -867,14 +938,14 @@ var StorageOAuthClientProvider = class {
|
|
|
867
938
|
async state() {
|
|
868
939
|
return this.sessionId;
|
|
869
940
|
}
|
|
870
|
-
async checkState(
|
|
941
|
+
async checkState(_state) {
|
|
871
942
|
const data = await storage.getSession(this.identity, this.sessionId);
|
|
872
943
|
if (!data) {
|
|
873
944
|
return { valid: false, error: "Session not found" };
|
|
874
945
|
}
|
|
875
946
|
return { valid: true, serverId: this.serverId };
|
|
876
947
|
}
|
|
877
|
-
async consumeState(
|
|
948
|
+
async consumeState(_state) {
|
|
878
949
|
}
|
|
879
950
|
async redirectToAuthorization(authUrl) {
|
|
880
951
|
this._authUrl = authUrl.toString();
|
|
@@ -886,7 +957,6 @@ var StorageOAuthClientProvider = class {
|
|
|
886
957
|
if (scope === "all") {
|
|
887
958
|
await storage.removeSession(this.identity, this.sessionId);
|
|
888
959
|
} else {
|
|
889
|
-
await this.getSessionData();
|
|
890
960
|
const updates = {};
|
|
891
961
|
if (scope === "client") {
|
|
892
962
|
updates.clientInformation = void 0;
|
|
@@ -1274,41 +1344,33 @@ var MCPClient = class _MCPClient {
|
|
|
1274
1344
|
if (!this.serverUrl || !this.callbackUrl || !this.serverId) {
|
|
1275
1345
|
throw new Error("Missing required connection metadata");
|
|
1276
1346
|
}
|
|
1277
|
-
const clientMetadata = {
|
|
1278
|
-
client_name: this.clientName || "MCP Assistant",
|
|
1279
|
-
redirect_uris: [this.callbackUrl],
|
|
1280
|
-
token_endpoint_auth_method: this.clientSecret ? "client_secret_basic" : "none",
|
|
1281
|
-
client_uri: this.clientUri || "https://mcp-assistant.in",
|
|
1282
|
-
logo_uri: this.logoUri || "https://mcp-assistant.in/logo.png",
|
|
1283
|
-
policy_uri: this.policyUri || "https://mcp-assistant.in/privacy",
|
|
1284
|
-
...this.clientId ? { client_id: this.clientId } : {},
|
|
1285
|
-
...this.clientSecret ? { client_secret: this.clientSecret } : {}
|
|
1286
|
-
};
|
|
1287
1347
|
if (!this.oauthProvider) {
|
|
1288
1348
|
if (!this.serverId) {
|
|
1289
1349
|
throw new Error("serverId required for OAuth provider initialization");
|
|
1290
1350
|
}
|
|
1291
|
-
this.oauthProvider = new StorageOAuthClientProvider(
|
|
1292
|
-
this.identity,
|
|
1293
|
-
this.serverId,
|
|
1294
|
-
this.sessionId,
|
|
1295
|
-
|
|
1296
|
-
this.
|
|
1297
|
-
|
|
1351
|
+
this.oauthProvider = new StorageOAuthClientProvider({
|
|
1352
|
+
identity: this.identity,
|
|
1353
|
+
serverId: this.serverId,
|
|
1354
|
+
sessionId: this.sessionId,
|
|
1355
|
+
redirectUrl: this.callbackUrl,
|
|
1356
|
+
clientName: this.clientName,
|
|
1357
|
+
clientUri: this.clientUri,
|
|
1358
|
+
logoUri: this.logoUri,
|
|
1359
|
+
policyUri: this.policyUri,
|
|
1360
|
+
clientId: this.clientId,
|
|
1361
|
+
clientSecret: this.clientSecret,
|
|
1362
|
+
onRedirect: (redirectUrl) => {
|
|
1298
1363
|
if (this.onRedirect) {
|
|
1299
1364
|
this.onRedirect(redirectUrl);
|
|
1300
1365
|
}
|
|
1301
1366
|
}
|
|
1302
|
-
);
|
|
1303
|
-
if (this.clientId && this.oauthProvider) {
|
|
1304
|
-
this.oauthProvider.clientId = this.clientId;
|
|
1305
|
-
}
|
|
1367
|
+
});
|
|
1306
1368
|
}
|
|
1307
1369
|
if (!this.client) {
|
|
1308
1370
|
this.client = new index_js.Client(
|
|
1309
1371
|
{
|
|
1310
|
-
name:
|
|
1311
|
-
version:
|
|
1372
|
+
name: MCP_CLIENT_NAME,
|
|
1373
|
+
version: MCP_CLIENT_VERSION
|
|
1312
1374
|
},
|
|
1313
1375
|
{
|
|
1314
1376
|
capabilities: {
|
|
@@ -1503,8 +1565,8 @@ var MCPClient = class _MCPClient {
|
|
|
1503
1565
|
this.emitProgress("Creating authenticated client...");
|
|
1504
1566
|
this.client = new index_js.Client(
|
|
1505
1567
|
{
|
|
1506
|
-
name:
|
|
1507
|
-
version:
|
|
1568
|
+
name: MCP_CLIENT_NAME,
|
|
1569
|
+
version: MCP_CLIENT_VERSION
|
|
1508
1570
|
},
|
|
1509
1571
|
{
|
|
1510
1572
|
capabilities: {
|
|
@@ -1799,8 +1861,8 @@ var MCPClient = class _MCPClient {
|
|
|
1799
1861
|
}
|
|
1800
1862
|
this.client = new index_js.Client(
|
|
1801
1863
|
{
|
|
1802
|
-
name:
|
|
1803
|
-
version:
|
|
1864
|
+
name: MCP_CLIENT_NAME,
|
|
1865
|
+
version: MCP_CLIENT_VERSION
|
|
1804
1866
|
},
|
|
1805
1867
|
{ capabilities: {} }
|
|
1806
1868
|
);
|
|
@@ -2069,6 +2131,27 @@ var MultiSessionClient = class {
|
|
|
2069
2131
|
}
|
|
2070
2132
|
};
|
|
2071
2133
|
|
|
2134
|
+
// src/shared/event-routing.ts
|
|
2135
|
+
function isRpcResponseEvent(event) {
|
|
2136
|
+
return "id" in event && ("result" in event || "error" in event);
|
|
2137
|
+
}
|
|
2138
|
+
function isConnectionEvent(event) {
|
|
2139
|
+
if (!("type" in event)) {
|
|
2140
|
+
return false;
|
|
2141
|
+
}
|
|
2142
|
+
switch (event.type) {
|
|
2143
|
+
case "state_changed":
|
|
2144
|
+
case "tools_discovered":
|
|
2145
|
+
case "auth_required":
|
|
2146
|
+
case "error":
|
|
2147
|
+
case "disconnected":
|
|
2148
|
+
case "progress":
|
|
2149
|
+
return true;
|
|
2150
|
+
default:
|
|
2151
|
+
return false;
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2072
2155
|
// src/server/handlers/sse-handler.ts
|
|
2073
2156
|
var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
|
|
2074
2157
|
var SSEConnectionManager = class {
|
|
@@ -2211,16 +2294,6 @@ var SSEConnectionManager = class {
|
|
|
2211
2294
|
throw new Error(`Connection already exists for server: ${duplicate.serverUrl || duplicate.serverId} (${duplicate.serverName})`);
|
|
2212
2295
|
}
|
|
2213
2296
|
const sessionId = await storage.generateSessionId();
|
|
2214
|
-
this.emitConnectionEvent({
|
|
2215
|
-
type: "state_changed",
|
|
2216
|
-
sessionId,
|
|
2217
|
-
serverId,
|
|
2218
|
-
serverName,
|
|
2219
|
-
serverUrl,
|
|
2220
|
-
state: "CONNECTING",
|
|
2221
|
-
previousState: "DISCONNECTED",
|
|
2222
|
-
timestamp: Date.now()
|
|
2223
|
-
});
|
|
2224
2297
|
try {
|
|
2225
2298
|
const clientMetadata = await this.getResolvedClientMetadata();
|
|
2226
2299
|
const client = new MCPClient({
|
|
@@ -2231,17 +2304,8 @@ var SSEConnectionManager = class {
|
|
|
2231
2304
|
serverUrl,
|
|
2232
2305
|
callbackUrl,
|
|
2233
2306
|
transportType,
|
|
2234
|
-
...clientMetadata
|
|
2307
|
+
...clientMetadata
|
|
2235
2308
|
// Spread client metadata (clientName, clientUri, logoUri, policyUri)
|
|
2236
|
-
onRedirect: (authUrl) => {
|
|
2237
|
-
this.emitConnectionEvent({
|
|
2238
|
-
type: "auth_required",
|
|
2239
|
-
sessionId,
|
|
2240
|
-
serverId,
|
|
2241
|
-
authUrl,
|
|
2242
|
-
timestamp: Date.now()
|
|
2243
|
-
});
|
|
2244
|
-
}
|
|
2245
2309
|
});
|
|
2246
2310
|
this.clients.set(sessionId, client);
|
|
2247
2311
|
client.onConnectionEvent((event) => {
|
|
@@ -2251,20 +2315,19 @@ var SSEConnectionManager = class {
|
|
|
2251
2315
|
this.sendEvent(event);
|
|
2252
2316
|
});
|
|
2253
2317
|
await client.connect();
|
|
2254
|
-
|
|
2255
|
-
this.emitConnectionEvent({
|
|
2256
|
-
type: "tools_discovered",
|
|
2257
|
-
sessionId,
|
|
2258
|
-
serverId,
|
|
2259
|
-
toolCount: tools.tools.length,
|
|
2260
|
-
tools: tools.tools,
|
|
2261
|
-
timestamp: Date.now()
|
|
2262
|
-
});
|
|
2318
|
+
await client.listTools();
|
|
2263
2319
|
return {
|
|
2264
2320
|
sessionId,
|
|
2265
2321
|
success: true
|
|
2266
2322
|
};
|
|
2267
2323
|
} catch (error) {
|
|
2324
|
+
if (error instanceof UnauthorizedError) {
|
|
2325
|
+
this.clients.delete(sessionId);
|
|
2326
|
+
return {
|
|
2327
|
+
sessionId,
|
|
2328
|
+
success: true
|
|
2329
|
+
};
|
|
2330
|
+
}
|
|
2268
2331
|
this.emitConnectionEvent({
|
|
2269
2332
|
type: "error",
|
|
2270
2333
|
sessionId,
|
|
@@ -2366,14 +2429,6 @@ var SSEConnectionManager = class {
|
|
|
2366
2429
|
await client.connect();
|
|
2367
2430
|
this.clients.set(sessionId, client);
|
|
2368
2431
|
const tools = await client.listTools();
|
|
2369
|
-
this.emitConnectionEvent({
|
|
2370
|
-
type: "tools_discovered",
|
|
2371
|
-
sessionId,
|
|
2372
|
-
serverId: session.serverId ?? "unknown",
|
|
2373
|
-
toolCount: tools.tools.length,
|
|
2374
|
-
tools: tools.tools,
|
|
2375
|
-
timestamp: Date.now()
|
|
2376
|
-
});
|
|
2377
2432
|
return { success: true, toolCount: tools.tools.length };
|
|
2378
2433
|
} catch (error) {
|
|
2379
2434
|
this.emitConnectionEvent({
|
|
@@ -2396,16 +2451,6 @@ var SSEConnectionManager = class {
|
|
|
2396
2451
|
if (!session) {
|
|
2397
2452
|
throw new Error("Session not found");
|
|
2398
2453
|
}
|
|
2399
|
-
this.emitConnectionEvent({
|
|
2400
|
-
type: "state_changed",
|
|
2401
|
-
sessionId,
|
|
2402
|
-
serverId: session.serverId ?? "unknown",
|
|
2403
|
-
serverName: session.serverName ?? "Unknown",
|
|
2404
|
-
serverUrl: session.serverUrl,
|
|
2405
|
-
state: "AUTHENTICATING",
|
|
2406
|
-
previousState: "DISCONNECTED",
|
|
2407
|
-
timestamp: Date.now()
|
|
2408
|
-
});
|
|
2409
2454
|
try {
|
|
2410
2455
|
const client = new MCPClient({
|
|
2411
2456
|
identity: this.identity,
|
|
@@ -2415,14 +2460,6 @@ var SSEConnectionManager = class {
|
|
|
2415
2460
|
await client.finishAuth(code);
|
|
2416
2461
|
this.clients.set(sessionId, client);
|
|
2417
2462
|
const tools = await client.listTools();
|
|
2418
|
-
this.emitConnectionEvent({
|
|
2419
|
-
type: "tools_discovered",
|
|
2420
|
-
sessionId,
|
|
2421
|
-
serverId: session.serverId ?? "unknown",
|
|
2422
|
-
toolCount: tools.tools.length,
|
|
2423
|
-
tools: tools.tools,
|
|
2424
|
-
timestamp: Date.now()
|
|
2425
|
-
});
|
|
2426
2463
|
return { success: true, toolCount: tools.tools.length };
|
|
2427
2464
|
} catch (error) {
|
|
2428
2465
|
this.emitConnectionEvent({
|
|
@@ -2500,9 +2537,9 @@ function createSSEHandler(options) {
|
|
|
2500
2537
|
});
|
|
2501
2538
|
writeSSEEvent(res, "connected", { timestamp: Date.now() });
|
|
2502
2539
|
const manager = new SSEConnectionManager(options, (event) => {
|
|
2503
|
-
if (
|
|
2540
|
+
if (isRpcResponseEvent(event)) {
|
|
2504
2541
|
writeSSEEvent(res, "rpc-response", event);
|
|
2505
|
-
} else if (
|
|
2542
|
+
} else if (isConnectionEvent(event)) {
|
|
2506
2543
|
writeSSEEvent(res, "connection", event);
|
|
2507
2544
|
} else {
|
|
2508
2545
|
writeSSEEvent(res, "observability", event);
|
|
@@ -2533,9 +2570,6 @@ function writeSSEEvent(res, event, data) {
|
|
|
2533
2570
|
}
|
|
2534
2571
|
|
|
2535
2572
|
// src/server/handlers/nextjs-handler.ts
|
|
2536
|
-
function isRpcResponseEvent(event) {
|
|
2537
|
-
return "id" in event && ("result" in event || "error" in event);
|
|
2538
|
-
}
|
|
2539
2573
|
function createNextMcpHandler(options = {}) {
|
|
2540
2574
|
const {
|
|
2541
2575
|
getIdentity = (request) => new URL(request.url).searchParams.get("identity"),
|
|
@@ -2626,7 +2660,7 @@ data: ${JSON.stringify(data)}
|
|
|
2626
2660
|
(event) => {
|
|
2627
2661
|
if (isRpcResponseEvent(event)) {
|
|
2628
2662
|
sendSSE("rpc-response", event);
|
|
2629
|
-
} else if (
|
|
2663
|
+
} else if (isConnectionEvent(event)) {
|
|
2630
2664
|
sendSSE("connection", event);
|
|
2631
2665
|
} else {
|
|
2632
2666
|
sendSSE("observability", event);
|
|
@@ -2683,6 +2717,7 @@ data: ${JSON.stringify(data)}
|
|
|
2683
2717
|
}
|
|
2684
2718
|
return { GET, POST };
|
|
2685
2719
|
}
|
|
2720
|
+
var CONNECTION_EVENT_INTERVAL_MS = 300;
|
|
2686
2721
|
var SSEClient = class {
|
|
2687
2722
|
constructor(options) {
|
|
2688
2723
|
this.options = options;
|
|
@@ -2787,10 +2822,12 @@ var SSEClient = class {
|
|
|
2787
2822
|
const data2 = await response.json();
|
|
2788
2823
|
return this.parseRpcResponse(data2);
|
|
2789
2824
|
}
|
|
2790
|
-
const data = await this.readRpcResponseFromStream(response
|
|
2825
|
+
const data = await this.readRpcResponseFromStream(response, {
|
|
2826
|
+
delayConnectionEvents: method === "connect" || method === "restoreSession" || method === "finishAuth"
|
|
2827
|
+
});
|
|
2791
2828
|
return this.parseRpcResponse(data);
|
|
2792
2829
|
}
|
|
2793
|
-
async readRpcResponseFromStream(response) {
|
|
2830
|
+
async readRpcResponseFromStream(response, options = {}) {
|
|
2794
2831
|
if (!response.body) {
|
|
2795
2832
|
throw new Error("Streaming response body is missing");
|
|
2796
2833
|
}
|
|
@@ -2798,7 +2835,7 @@ var SSEClient = class {
|
|
|
2798
2835
|
const decoder = new TextDecoder();
|
|
2799
2836
|
let buffer = "";
|
|
2800
2837
|
let rpcResponse = null;
|
|
2801
|
-
const dispatchBlock = (block) => {
|
|
2838
|
+
const dispatchBlock = async (block) => {
|
|
2802
2839
|
const lines = block.split("\n");
|
|
2803
2840
|
let eventName = "message";
|
|
2804
2841
|
const dataLines = [];
|
|
@@ -2826,6 +2863,9 @@ var SSEClient = class {
|
|
|
2826
2863
|
break;
|
|
2827
2864
|
case "connection":
|
|
2828
2865
|
this.options.onConnectionEvent?.(payload);
|
|
2866
|
+
if (options.delayConnectionEvents) {
|
|
2867
|
+
await this.sleep(CONNECTION_EVENT_INTERVAL_MS);
|
|
2868
|
+
}
|
|
2829
2869
|
break;
|
|
2830
2870
|
case "observability":
|
|
2831
2871
|
this.options.onObservabilityEvent?.(payload);
|
|
@@ -2845,18 +2885,21 @@ var SSEClient = class {
|
|
|
2845
2885
|
const separatorLength = separatorMatch[0].length;
|
|
2846
2886
|
const block = buffer.slice(0, separatorIndex);
|
|
2847
2887
|
buffer = buffer.slice(separatorIndex + separatorLength);
|
|
2848
|
-
dispatchBlock(block);
|
|
2888
|
+
await dispatchBlock(block);
|
|
2849
2889
|
separatorMatch = buffer.match(/\r?\n\r?\n/);
|
|
2850
2890
|
}
|
|
2851
2891
|
}
|
|
2852
2892
|
if (buffer.trim()) {
|
|
2853
|
-
dispatchBlock(buffer);
|
|
2893
|
+
await dispatchBlock(buffer);
|
|
2854
2894
|
}
|
|
2855
2895
|
if (!rpcResponse) {
|
|
2856
2896
|
throw new Error("Missing rpc-response event in streamed RPC result");
|
|
2857
2897
|
}
|
|
2858
2898
|
return rpcResponse;
|
|
2859
2899
|
}
|
|
2900
|
+
sleep(ms) {
|
|
2901
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2902
|
+
}
|
|
2860
2903
|
parseRpcResponse(data) {
|
|
2861
2904
|
if ("result" in data) {
|
|
2862
2905
|
return data.result;
|