@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.mjs
CHANGED
|
@@ -120,10 +120,10 @@ var REDIS_KEY_PREFIX = "mcp:session:";
|
|
|
120
120
|
var TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1e3;
|
|
121
121
|
var DEFAULT_CLIENT_NAME = "MCP Assistant";
|
|
122
122
|
var DEFAULT_CLIENT_URI = "https://mcp-assistant.in";
|
|
123
|
-
var DEFAULT_LOGO_URI = "https://mcp-assistant.in/logo.
|
|
123
|
+
var DEFAULT_LOGO_URI = "https://mcp-assistant.in/logo.svg";
|
|
124
124
|
var DEFAULT_POLICY_URI = "https://mcp-assistant.in/privacy";
|
|
125
125
|
var SOFTWARE_ID = "@mcp-ts";
|
|
126
|
-
var SOFTWARE_VERSION = "1.
|
|
126
|
+
var SOFTWARE_VERSION = "1.3.4";
|
|
127
127
|
var MCP_CLIENT_NAME = "mcp-ts-oauth-client";
|
|
128
128
|
var MCP_CLIENT_VERSION = "2.0";
|
|
129
129
|
|
|
@@ -141,6 +141,8 @@ var RedisStorageBackend = class {
|
|
|
141
141
|
this.redis = redis2;
|
|
142
142
|
__publicField(this, "DEFAULT_TTL", SESSION_TTL_SECONDS);
|
|
143
143
|
__publicField(this, "KEY_PREFIX", "mcp:session:");
|
|
144
|
+
__publicField(this, "IDENTITY_KEY_PREFIX", "mcp:identity:");
|
|
145
|
+
__publicField(this, "IDENTITY_KEY_SUFFIX", ":sessions");
|
|
144
146
|
}
|
|
145
147
|
/**
|
|
146
148
|
* Generates Redis key for a specific session
|
|
@@ -154,7 +156,34 @@ var RedisStorageBackend = class {
|
|
|
154
156
|
* @private
|
|
155
157
|
*/
|
|
156
158
|
getIdentityKey(identity) {
|
|
157
|
-
return
|
|
159
|
+
return `${this.IDENTITY_KEY_PREFIX}${identity}${this.IDENTITY_KEY_SUFFIX}`;
|
|
160
|
+
}
|
|
161
|
+
parseIdentityFromKey(identityKey) {
|
|
162
|
+
return identityKey.slice(
|
|
163
|
+
this.IDENTITY_KEY_PREFIX.length,
|
|
164
|
+
identityKey.length - this.IDENTITY_KEY_SUFFIX.length
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
async scanKeys(pattern) {
|
|
168
|
+
const redis2 = this.redis;
|
|
169
|
+
if (typeof redis2.scan !== "function") {
|
|
170
|
+
return await this.redis.keys(pattern);
|
|
171
|
+
}
|
|
172
|
+
const keys = /* @__PURE__ */ new Set();
|
|
173
|
+
let cursor = "0";
|
|
174
|
+
try {
|
|
175
|
+
do {
|
|
176
|
+
const [nextCursor, batch] = await redis2.scan(cursor, "MATCH", pattern, "COUNT", 100);
|
|
177
|
+
cursor = nextCursor;
|
|
178
|
+
for (const key of batch) {
|
|
179
|
+
keys.add(key);
|
|
180
|
+
}
|
|
181
|
+
} while (cursor !== "0");
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.warn("[RedisStorage] SCAN failed, falling back to KEYS:", error);
|
|
184
|
+
return await this.redis.keys(pattern);
|
|
185
|
+
}
|
|
186
|
+
return Array.from(keys);
|
|
158
187
|
}
|
|
159
188
|
generateSessionId() {
|
|
160
189
|
return firstChar() + rest();
|
|
@@ -222,17 +251,13 @@ var RedisStorageBackend = class {
|
|
|
222
251
|
}
|
|
223
252
|
}
|
|
224
253
|
async getIdentityMcpSessions(identity) {
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
return await this.redis.smembers(identityKey);
|
|
228
|
-
} catch (error) {
|
|
229
|
-
console.error(`[RedisStorage] Failed to get sessions for ${identity}:`, error);
|
|
230
|
-
return [];
|
|
231
|
-
}
|
|
254
|
+
const sessions = await this.getIdentitySessionsData(identity);
|
|
255
|
+
return sessions.map((session) => session.sessionId);
|
|
232
256
|
}
|
|
233
257
|
async getIdentitySessionsData(identity) {
|
|
234
258
|
try {
|
|
235
|
-
const
|
|
259
|
+
const identityKey = this.getIdentityKey(identity);
|
|
260
|
+
const sessionIds = await this.redis.smembers(identityKey);
|
|
236
261
|
if (sessionIds.length === 0) return [];
|
|
237
262
|
const results = await Promise.all(
|
|
238
263
|
sessionIds.map(async (sessionId) => {
|
|
@@ -240,6 +265,10 @@ var RedisStorageBackend = class {
|
|
|
240
265
|
return data ? JSON.parse(data) : null;
|
|
241
266
|
})
|
|
242
267
|
);
|
|
268
|
+
const staleSessionIds = sessionIds.filter((_, index) => results[index] === null);
|
|
269
|
+
if (staleSessionIds.length > 0) {
|
|
270
|
+
await this.redis.srem(identityKey, ...staleSessionIds);
|
|
271
|
+
}
|
|
243
272
|
return results.filter((session) => session !== null);
|
|
244
273
|
} catch (error) {
|
|
245
274
|
console.error(`[RedisStorage] Failed to get session data for ${identity}:`, error);
|
|
@@ -258,9 +287,22 @@ var RedisStorageBackend = class {
|
|
|
258
287
|
}
|
|
259
288
|
async getAllSessionIds() {
|
|
260
289
|
try {
|
|
261
|
-
const
|
|
262
|
-
const
|
|
263
|
-
|
|
290
|
+
const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
|
|
291
|
+
const sessions = await Promise.all(
|
|
292
|
+
keys.map(async (key) => {
|
|
293
|
+
const data = await this.redis.get(key);
|
|
294
|
+
if (!data) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
return JSON.parse(data).sessionId;
|
|
299
|
+
} catch (error) {
|
|
300
|
+
console.error("[RedisStorage] Failed to parse session while listing all session IDs:", error);
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
);
|
|
305
|
+
return sessions.filter((sessionId) => sessionId !== null);
|
|
264
306
|
} catch (error) {
|
|
265
307
|
console.error("[RedisStorage] Failed to get all sessions:", error);
|
|
266
308
|
return [];
|
|
@@ -268,10 +310,11 @@ var RedisStorageBackend = class {
|
|
|
268
310
|
}
|
|
269
311
|
async clearAll() {
|
|
270
312
|
try {
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
313
|
+
const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
|
|
314
|
+
const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
|
|
315
|
+
const allKeys = [...keys, ...identityKeys];
|
|
316
|
+
if (allKeys.length > 0) {
|
|
317
|
+
await this.redis.del(...allKeys);
|
|
275
318
|
}
|
|
276
319
|
} catch (error) {
|
|
277
320
|
console.error("[RedisStorage] Failed to clear sessions:", error);
|
|
@@ -279,12 +322,24 @@ var RedisStorageBackend = class {
|
|
|
279
322
|
}
|
|
280
323
|
async cleanupExpiredSessions() {
|
|
281
324
|
try {
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
const
|
|
286
|
-
if (
|
|
287
|
-
await this.redis.del(
|
|
325
|
+
const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
|
|
326
|
+
for (const identityKey of identityKeys) {
|
|
327
|
+
const identity = this.parseIdentityFromKey(identityKey);
|
|
328
|
+
const sessionIds = await this.redis.smembers(identityKey);
|
|
329
|
+
if (sessionIds.length === 0) {
|
|
330
|
+
await this.redis.del(identityKey);
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
const existenceChecks = await Promise.all(
|
|
334
|
+
sessionIds.map((sessionId) => this.redis.exists(this.getSessionKey(identity, sessionId)))
|
|
335
|
+
);
|
|
336
|
+
const staleSessionIds = sessionIds.filter((_, index) => existenceChecks[index] === 0);
|
|
337
|
+
if (staleSessionIds.length > 0) {
|
|
338
|
+
await this.redis.srem(identityKey, ...staleSessionIds);
|
|
339
|
+
}
|
|
340
|
+
const remainingCount = await this.redis.scard(identityKey);
|
|
341
|
+
if (remainingCount === 0) {
|
|
342
|
+
await this.redis.del(identityKey);
|
|
288
343
|
}
|
|
289
344
|
}
|
|
290
345
|
} catch (error) {
|
|
@@ -655,6 +710,12 @@ var SqliteStorage = class {
|
|
|
655
710
|
// src/server/storage/index.ts
|
|
656
711
|
var storageInstance = null;
|
|
657
712
|
var storagePromise = null;
|
|
713
|
+
async function initializeStorage(store) {
|
|
714
|
+
if (typeof store.init === "function") {
|
|
715
|
+
await store.init();
|
|
716
|
+
}
|
|
717
|
+
return store;
|
|
718
|
+
}
|
|
658
719
|
async function createStorage() {
|
|
659
720
|
const type = process.env.MCP_TS_STORAGE_TYPE?.toLowerCase();
|
|
660
721
|
if (type === "redis") {
|
|
@@ -678,16 +739,12 @@ async function createStorage() {
|
|
|
678
739
|
console.warn('[Storage] MCP_TS_STORAGE_TYPE is "file" but MCP_TS_STORAGE_FILE is missing');
|
|
679
740
|
}
|
|
680
741
|
console.log(`[Storage] Using File storage (${filePath}) (Explicit)`);
|
|
681
|
-
|
|
682
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
683
|
-
return store;
|
|
742
|
+
return await initializeStorage(new FileStorageBackend({ path: filePath }));
|
|
684
743
|
}
|
|
685
744
|
if (type === "sqlite") {
|
|
686
745
|
const dbPath = process.env.MCP_TS_STORAGE_SQLITE_PATH;
|
|
687
746
|
console.log(`[Storage] Using SQLite storage (${dbPath || "default"}) (Explicit)`);
|
|
688
|
-
|
|
689
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
690
|
-
return store;
|
|
747
|
+
return await initializeStorage(new SqliteStorage({ path: dbPath }));
|
|
691
748
|
}
|
|
692
749
|
if (type === "memory") {
|
|
693
750
|
console.log("[Storage] Using In-Memory storage (Explicit)");
|
|
@@ -707,15 +764,11 @@ async function createStorage() {
|
|
|
707
764
|
}
|
|
708
765
|
if (process.env.MCP_TS_STORAGE_FILE) {
|
|
709
766
|
console.log(`[Storage] Auto-detected MCP_TS_STORAGE_FILE. Using File storage (${process.env.MCP_TS_STORAGE_FILE}).`);
|
|
710
|
-
|
|
711
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
712
|
-
return store;
|
|
767
|
+
return await initializeStorage(new FileStorageBackend({ path: process.env.MCP_TS_STORAGE_FILE }));
|
|
713
768
|
}
|
|
714
769
|
if (process.env.MCP_TS_STORAGE_SQLITE_PATH) {
|
|
715
770
|
console.log(`[Storage] Auto-detected MCP_TS_STORAGE_SQLITE_PATH. Using SQLite storage (${process.env.MCP_TS_STORAGE_SQLITE_PATH}).`);
|
|
716
|
-
|
|
717
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
718
|
-
return store;
|
|
771
|
+
return await initializeStorage(new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH }));
|
|
719
772
|
}
|
|
720
773
|
console.log("[Storage] No storage configured. Using In-Memory storage (Default).");
|
|
721
774
|
return new MemoryStorageBackend();
|
|
@@ -725,7 +778,10 @@ async function getStorage() {
|
|
|
725
778
|
return storageInstance;
|
|
726
779
|
}
|
|
727
780
|
if (!storagePromise) {
|
|
728
|
-
storagePromise = createStorage()
|
|
781
|
+
storagePromise = createStorage().catch((error) => {
|
|
782
|
+
storagePromise = null;
|
|
783
|
+
throw error;
|
|
784
|
+
});
|
|
729
785
|
}
|
|
730
786
|
storageInstance = await storagePromise;
|
|
731
787
|
return storageInstance;
|
|
@@ -746,43 +802,49 @@ var storage = new Proxy({}, {
|
|
|
746
802
|
// src/server/mcp/storage-oauth-provider.ts
|
|
747
803
|
var StorageOAuthClientProvider = class {
|
|
748
804
|
/**
|
|
749
|
-
* Creates a new
|
|
750
|
-
* @param
|
|
751
|
-
* @param serverId - Server identifier (for tracking which server this OAuth session belongs to)
|
|
752
|
-
* @param sessionId - Session identifier (used as OAuth state)
|
|
753
|
-
* @param clientName - OAuth client name
|
|
754
|
-
* @param baseRedirectUrl - OAuth callback URL
|
|
755
|
-
* @param onRedirect - Optional callback when redirect to authorization is needed
|
|
805
|
+
* Creates a new storage-backed OAuth provider
|
|
806
|
+
* @param options - Provider configuration
|
|
756
807
|
*/
|
|
757
|
-
constructor(
|
|
758
|
-
this
|
|
759
|
-
this
|
|
760
|
-
this
|
|
761
|
-
this
|
|
762
|
-
this
|
|
808
|
+
constructor(options) {
|
|
809
|
+
__publicField(this, "identity");
|
|
810
|
+
__publicField(this, "serverId");
|
|
811
|
+
__publicField(this, "sessionId");
|
|
812
|
+
__publicField(this, "redirectUrl");
|
|
813
|
+
__publicField(this, "clientName");
|
|
814
|
+
__publicField(this, "clientUri");
|
|
815
|
+
__publicField(this, "logoUri");
|
|
816
|
+
__publicField(this, "policyUri");
|
|
817
|
+
__publicField(this, "clientSecret");
|
|
763
818
|
__publicField(this, "_authUrl");
|
|
764
819
|
__publicField(this, "_clientId");
|
|
765
820
|
__publicField(this, "onRedirectCallback");
|
|
766
821
|
__publicField(this, "tokenExpiresAt");
|
|
767
|
-
this.
|
|
822
|
+
this.identity = options.identity;
|
|
823
|
+
this.serverId = options.serverId;
|
|
824
|
+
this.sessionId = options.sessionId;
|
|
825
|
+
this.redirectUrl = options.redirectUrl;
|
|
826
|
+
this.clientName = options.clientName;
|
|
827
|
+
this.clientUri = options.clientUri;
|
|
828
|
+
this.logoUri = options.logoUri;
|
|
829
|
+
this.policyUri = options.policyUri;
|
|
830
|
+
this._clientId = options.clientId;
|
|
831
|
+
this.clientSecret = options.clientSecret;
|
|
832
|
+
this.onRedirectCallback = options.onRedirect;
|
|
768
833
|
}
|
|
769
834
|
get clientMetadata() {
|
|
770
835
|
return {
|
|
771
|
-
client_name: this.clientName,
|
|
772
|
-
client_uri: this.clientUri,
|
|
836
|
+
client_name: this.clientName || DEFAULT_CLIENT_NAME,
|
|
837
|
+
client_uri: this.clientUri || DEFAULT_CLIENT_URI,
|
|
838
|
+
logo_uri: this.logoUri || DEFAULT_LOGO_URI,
|
|
839
|
+
policy_uri: this.policyUri || DEFAULT_POLICY_URI,
|
|
773
840
|
grant_types: ["authorization_code", "refresh_token"],
|
|
774
841
|
redirect_uris: [this.redirectUrl],
|
|
775
842
|
response_types: ["code"],
|
|
776
|
-
token_endpoint_auth_method: "none",
|
|
777
|
-
|
|
843
|
+
token_endpoint_auth_method: this.clientSecret ? "client_secret_basic" : "none",
|
|
844
|
+
software_id: SOFTWARE_ID,
|
|
845
|
+
software_version: SOFTWARE_VERSION
|
|
778
846
|
};
|
|
779
847
|
}
|
|
780
|
-
get clientUri() {
|
|
781
|
-
return new URL(this.redirectUrl).origin;
|
|
782
|
-
}
|
|
783
|
-
get redirectUrl() {
|
|
784
|
-
return this.baseRedirectUrl;
|
|
785
|
-
}
|
|
786
848
|
get clientId() {
|
|
787
849
|
return this._clientId;
|
|
788
850
|
}
|
|
@@ -817,7 +879,16 @@ var StorageOAuthClientProvider = class {
|
|
|
817
879
|
if (data.clientId && !this._clientId) {
|
|
818
880
|
this._clientId = data.clientId;
|
|
819
881
|
}
|
|
820
|
-
|
|
882
|
+
if (data.clientInformation) {
|
|
883
|
+
return data.clientInformation;
|
|
884
|
+
}
|
|
885
|
+
if (!this._clientId) {
|
|
886
|
+
return void 0;
|
|
887
|
+
}
|
|
888
|
+
return {
|
|
889
|
+
client_id: this._clientId,
|
|
890
|
+
...this.clientSecret ? { client_secret: this.clientSecret } : {}
|
|
891
|
+
};
|
|
821
892
|
}
|
|
822
893
|
/**
|
|
823
894
|
* Stores OAuth client information
|
|
@@ -845,14 +916,14 @@ var StorageOAuthClientProvider = class {
|
|
|
845
916
|
async state() {
|
|
846
917
|
return this.sessionId;
|
|
847
918
|
}
|
|
848
|
-
async checkState(
|
|
919
|
+
async checkState(_state) {
|
|
849
920
|
const data = await storage.getSession(this.identity, this.sessionId);
|
|
850
921
|
if (!data) {
|
|
851
922
|
return { valid: false, error: "Session not found" };
|
|
852
923
|
}
|
|
853
924
|
return { valid: true, serverId: this.serverId };
|
|
854
925
|
}
|
|
855
|
-
async consumeState(
|
|
926
|
+
async consumeState(_state) {
|
|
856
927
|
}
|
|
857
928
|
async redirectToAuthorization(authUrl) {
|
|
858
929
|
this._authUrl = authUrl.toString();
|
|
@@ -864,7 +935,6 @@ var StorageOAuthClientProvider = class {
|
|
|
864
935
|
if (scope === "all") {
|
|
865
936
|
await storage.removeSession(this.identity, this.sessionId);
|
|
866
937
|
} else {
|
|
867
|
-
await this.getSessionData();
|
|
868
938
|
const updates = {};
|
|
869
939
|
if (scope === "client") {
|
|
870
940
|
updates.clientInformation = void 0;
|
|
@@ -1252,41 +1322,33 @@ var MCPClient = class _MCPClient {
|
|
|
1252
1322
|
if (!this.serverUrl || !this.callbackUrl || !this.serverId) {
|
|
1253
1323
|
throw new Error("Missing required connection metadata");
|
|
1254
1324
|
}
|
|
1255
|
-
const clientMetadata = {
|
|
1256
|
-
client_name: this.clientName || "MCP Assistant",
|
|
1257
|
-
redirect_uris: [this.callbackUrl],
|
|
1258
|
-
token_endpoint_auth_method: this.clientSecret ? "client_secret_basic" : "none",
|
|
1259
|
-
client_uri: this.clientUri || "https://mcp-assistant.in",
|
|
1260
|
-
logo_uri: this.logoUri || "https://mcp-assistant.in/logo.png",
|
|
1261
|
-
policy_uri: this.policyUri || "https://mcp-assistant.in/privacy",
|
|
1262
|
-
...this.clientId ? { client_id: this.clientId } : {},
|
|
1263
|
-
...this.clientSecret ? { client_secret: this.clientSecret } : {}
|
|
1264
|
-
};
|
|
1265
1325
|
if (!this.oauthProvider) {
|
|
1266
1326
|
if (!this.serverId) {
|
|
1267
1327
|
throw new Error("serverId required for OAuth provider initialization");
|
|
1268
1328
|
}
|
|
1269
|
-
this.oauthProvider = new StorageOAuthClientProvider(
|
|
1270
|
-
this.identity,
|
|
1271
|
-
this.serverId,
|
|
1272
|
-
this.sessionId,
|
|
1273
|
-
|
|
1274
|
-
this.
|
|
1275
|
-
|
|
1329
|
+
this.oauthProvider = new StorageOAuthClientProvider({
|
|
1330
|
+
identity: this.identity,
|
|
1331
|
+
serverId: this.serverId,
|
|
1332
|
+
sessionId: this.sessionId,
|
|
1333
|
+
redirectUrl: this.callbackUrl,
|
|
1334
|
+
clientName: this.clientName,
|
|
1335
|
+
clientUri: this.clientUri,
|
|
1336
|
+
logoUri: this.logoUri,
|
|
1337
|
+
policyUri: this.policyUri,
|
|
1338
|
+
clientId: this.clientId,
|
|
1339
|
+
clientSecret: this.clientSecret,
|
|
1340
|
+
onRedirect: (redirectUrl) => {
|
|
1276
1341
|
if (this.onRedirect) {
|
|
1277
1342
|
this.onRedirect(redirectUrl);
|
|
1278
1343
|
}
|
|
1279
1344
|
}
|
|
1280
|
-
);
|
|
1281
|
-
if (this.clientId && this.oauthProvider) {
|
|
1282
|
-
this.oauthProvider.clientId = this.clientId;
|
|
1283
|
-
}
|
|
1345
|
+
});
|
|
1284
1346
|
}
|
|
1285
1347
|
if (!this.client) {
|
|
1286
1348
|
this.client = new Client(
|
|
1287
1349
|
{
|
|
1288
|
-
name:
|
|
1289
|
-
version:
|
|
1350
|
+
name: MCP_CLIENT_NAME,
|
|
1351
|
+
version: MCP_CLIENT_VERSION
|
|
1290
1352
|
},
|
|
1291
1353
|
{
|
|
1292
1354
|
capabilities: {
|
|
@@ -1481,8 +1543,8 @@ var MCPClient = class _MCPClient {
|
|
|
1481
1543
|
this.emitProgress("Creating authenticated client...");
|
|
1482
1544
|
this.client = new Client(
|
|
1483
1545
|
{
|
|
1484
|
-
name:
|
|
1485
|
-
version:
|
|
1546
|
+
name: MCP_CLIENT_NAME,
|
|
1547
|
+
version: MCP_CLIENT_VERSION
|
|
1486
1548
|
},
|
|
1487
1549
|
{
|
|
1488
1550
|
capabilities: {
|
|
@@ -1777,8 +1839,8 @@ var MCPClient = class _MCPClient {
|
|
|
1777
1839
|
}
|
|
1778
1840
|
this.client = new Client(
|
|
1779
1841
|
{
|
|
1780
|
-
name:
|
|
1781
|
-
version:
|
|
1842
|
+
name: MCP_CLIENT_NAME,
|
|
1843
|
+
version: MCP_CLIENT_VERSION
|
|
1782
1844
|
},
|
|
1783
1845
|
{ capabilities: {} }
|
|
1784
1846
|
);
|
|
@@ -2047,6 +2109,27 @@ var MultiSessionClient = class {
|
|
|
2047
2109
|
}
|
|
2048
2110
|
};
|
|
2049
2111
|
|
|
2112
|
+
// src/shared/event-routing.ts
|
|
2113
|
+
function isRpcResponseEvent(event) {
|
|
2114
|
+
return "id" in event && ("result" in event || "error" in event);
|
|
2115
|
+
}
|
|
2116
|
+
function isConnectionEvent(event) {
|
|
2117
|
+
if (!("type" in event)) {
|
|
2118
|
+
return false;
|
|
2119
|
+
}
|
|
2120
|
+
switch (event.type) {
|
|
2121
|
+
case "state_changed":
|
|
2122
|
+
case "tools_discovered":
|
|
2123
|
+
case "auth_required":
|
|
2124
|
+
case "error":
|
|
2125
|
+
case "disconnected":
|
|
2126
|
+
case "progress":
|
|
2127
|
+
return true;
|
|
2128
|
+
default:
|
|
2129
|
+
return false;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2050
2133
|
// src/server/handlers/sse-handler.ts
|
|
2051
2134
|
var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
|
|
2052
2135
|
var SSEConnectionManager = class {
|
|
@@ -2189,16 +2272,6 @@ var SSEConnectionManager = class {
|
|
|
2189
2272
|
throw new Error(`Connection already exists for server: ${duplicate.serverUrl || duplicate.serverId} (${duplicate.serverName})`);
|
|
2190
2273
|
}
|
|
2191
2274
|
const sessionId = await storage.generateSessionId();
|
|
2192
|
-
this.emitConnectionEvent({
|
|
2193
|
-
type: "state_changed",
|
|
2194
|
-
sessionId,
|
|
2195
|
-
serverId,
|
|
2196
|
-
serverName,
|
|
2197
|
-
serverUrl,
|
|
2198
|
-
state: "CONNECTING",
|
|
2199
|
-
previousState: "DISCONNECTED",
|
|
2200
|
-
timestamp: Date.now()
|
|
2201
|
-
});
|
|
2202
2275
|
try {
|
|
2203
2276
|
const clientMetadata = await this.getResolvedClientMetadata();
|
|
2204
2277
|
const client = new MCPClient({
|
|
@@ -2209,17 +2282,8 @@ var SSEConnectionManager = class {
|
|
|
2209
2282
|
serverUrl,
|
|
2210
2283
|
callbackUrl,
|
|
2211
2284
|
transportType,
|
|
2212
|
-
...clientMetadata
|
|
2285
|
+
...clientMetadata
|
|
2213
2286
|
// Spread client metadata (clientName, clientUri, logoUri, policyUri)
|
|
2214
|
-
onRedirect: (authUrl) => {
|
|
2215
|
-
this.emitConnectionEvent({
|
|
2216
|
-
type: "auth_required",
|
|
2217
|
-
sessionId,
|
|
2218
|
-
serverId,
|
|
2219
|
-
authUrl,
|
|
2220
|
-
timestamp: Date.now()
|
|
2221
|
-
});
|
|
2222
|
-
}
|
|
2223
2287
|
});
|
|
2224
2288
|
this.clients.set(sessionId, client);
|
|
2225
2289
|
client.onConnectionEvent((event) => {
|
|
@@ -2229,20 +2293,19 @@ var SSEConnectionManager = class {
|
|
|
2229
2293
|
this.sendEvent(event);
|
|
2230
2294
|
});
|
|
2231
2295
|
await client.connect();
|
|
2232
|
-
|
|
2233
|
-
this.emitConnectionEvent({
|
|
2234
|
-
type: "tools_discovered",
|
|
2235
|
-
sessionId,
|
|
2236
|
-
serverId,
|
|
2237
|
-
toolCount: tools.tools.length,
|
|
2238
|
-
tools: tools.tools,
|
|
2239
|
-
timestamp: Date.now()
|
|
2240
|
-
});
|
|
2296
|
+
await client.listTools();
|
|
2241
2297
|
return {
|
|
2242
2298
|
sessionId,
|
|
2243
2299
|
success: true
|
|
2244
2300
|
};
|
|
2245
2301
|
} catch (error) {
|
|
2302
|
+
if (error instanceof UnauthorizedError) {
|
|
2303
|
+
this.clients.delete(sessionId);
|
|
2304
|
+
return {
|
|
2305
|
+
sessionId,
|
|
2306
|
+
success: true
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2246
2309
|
this.emitConnectionEvent({
|
|
2247
2310
|
type: "error",
|
|
2248
2311
|
sessionId,
|
|
@@ -2344,14 +2407,6 @@ var SSEConnectionManager = class {
|
|
|
2344
2407
|
await client.connect();
|
|
2345
2408
|
this.clients.set(sessionId, client);
|
|
2346
2409
|
const tools = await client.listTools();
|
|
2347
|
-
this.emitConnectionEvent({
|
|
2348
|
-
type: "tools_discovered",
|
|
2349
|
-
sessionId,
|
|
2350
|
-
serverId: session.serverId ?? "unknown",
|
|
2351
|
-
toolCount: tools.tools.length,
|
|
2352
|
-
tools: tools.tools,
|
|
2353
|
-
timestamp: Date.now()
|
|
2354
|
-
});
|
|
2355
2410
|
return { success: true, toolCount: tools.tools.length };
|
|
2356
2411
|
} catch (error) {
|
|
2357
2412
|
this.emitConnectionEvent({
|
|
@@ -2374,16 +2429,6 @@ var SSEConnectionManager = class {
|
|
|
2374
2429
|
if (!session) {
|
|
2375
2430
|
throw new Error("Session not found");
|
|
2376
2431
|
}
|
|
2377
|
-
this.emitConnectionEvent({
|
|
2378
|
-
type: "state_changed",
|
|
2379
|
-
sessionId,
|
|
2380
|
-
serverId: session.serverId ?? "unknown",
|
|
2381
|
-
serverName: session.serverName ?? "Unknown",
|
|
2382
|
-
serverUrl: session.serverUrl,
|
|
2383
|
-
state: "AUTHENTICATING",
|
|
2384
|
-
previousState: "DISCONNECTED",
|
|
2385
|
-
timestamp: Date.now()
|
|
2386
|
-
});
|
|
2387
2432
|
try {
|
|
2388
2433
|
const client = new MCPClient({
|
|
2389
2434
|
identity: this.identity,
|
|
@@ -2393,14 +2438,6 @@ var SSEConnectionManager = class {
|
|
|
2393
2438
|
await client.finishAuth(code);
|
|
2394
2439
|
this.clients.set(sessionId, client);
|
|
2395
2440
|
const tools = await client.listTools();
|
|
2396
|
-
this.emitConnectionEvent({
|
|
2397
|
-
type: "tools_discovered",
|
|
2398
|
-
sessionId,
|
|
2399
|
-
serverId: session.serverId ?? "unknown",
|
|
2400
|
-
toolCount: tools.tools.length,
|
|
2401
|
-
tools: tools.tools,
|
|
2402
|
-
timestamp: Date.now()
|
|
2403
|
-
});
|
|
2404
2441
|
return { success: true, toolCount: tools.tools.length };
|
|
2405
2442
|
} catch (error) {
|
|
2406
2443
|
this.emitConnectionEvent({
|
|
@@ -2478,9 +2515,9 @@ function createSSEHandler(options) {
|
|
|
2478
2515
|
});
|
|
2479
2516
|
writeSSEEvent(res, "connected", { timestamp: Date.now() });
|
|
2480
2517
|
const manager = new SSEConnectionManager(options, (event) => {
|
|
2481
|
-
if (
|
|
2518
|
+
if (isRpcResponseEvent(event)) {
|
|
2482
2519
|
writeSSEEvent(res, "rpc-response", event);
|
|
2483
|
-
} else if (
|
|
2520
|
+
} else if (isConnectionEvent(event)) {
|
|
2484
2521
|
writeSSEEvent(res, "connection", event);
|
|
2485
2522
|
} else {
|
|
2486
2523
|
writeSSEEvent(res, "observability", event);
|
|
@@ -2511,9 +2548,6 @@ function writeSSEEvent(res, event, data) {
|
|
|
2511
2548
|
}
|
|
2512
2549
|
|
|
2513
2550
|
// src/server/handlers/nextjs-handler.ts
|
|
2514
|
-
function isRpcResponseEvent(event) {
|
|
2515
|
-
return "id" in event && ("result" in event || "error" in event);
|
|
2516
|
-
}
|
|
2517
2551
|
function createNextMcpHandler(options = {}) {
|
|
2518
2552
|
const {
|
|
2519
2553
|
getIdentity = (request) => new URL(request.url).searchParams.get("identity"),
|
|
@@ -2604,7 +2638,7 @@ data: ${JSON.stringify(data)}
|
|
|
2604
2638
|
(event) => {
|
|
2605
2639
|
if (isRpcResponseEvent(event)) {
|
|
2606
2640
|
sendSSE("rpc-response", event);
|
|
2607
|
-
} else if (
|
|
2641
|
+
} else if (isConnectionEvent(event)) {
|
|
2608
2642
|
sendSSE("connection", event);
|
|
2609
2643
|
} else {
|
|
2610
2644
|
sendSSE("observability", event);
|
|
@@ -2661,6 +2695,7 @@ data: ${JSON.stringify(data)}
|
|
|
2661
2695
|
}
|
|
2662
2696
|
return { GET, POST };
|
|
2663
2697
|
}
|
|
2698
|
+
var CONNECTION_EVENT_INTERVAL_MS = 300;
|
|
2664
2699
|
var SSEClient = class {
|
|
2665
2700
|
constructor(options) {
|
|
2666
2701
|
this.options = options;
|
|
@@ -2765,10 +2800,12 @@ var SSEClient = class {
|
|
|
2765
2800
|
const data2 = await response.json();
|
|
2766
2801
|
return this.parseRpcResponse(data2);
|
|
2767
2802
|
}
|
|
2768
|
-
const data = await this.readRpcResponseFromStream(response
|
|
2803
|
+
const data = await this.readRpcResponseFromStream(response, {
|
|
2804
|
+
delayConnectionEvents: method === "connect" || method === "restoreSession" || method === "finishAuth"
|
|
2805
|
+
});
|
|
2769
2806
|
return this.parseRpcResponse(data);
|
|
2770
2807
|
}
|
|
2771
|
-
async readRpcResponseFromStream(response) {
|
|
2808
|
+
async readRpcResponseFromStream(response, options = {}) {
|
|
2772
2809
|
if (!response.body) {
|
|
2773
2810
|
throw new Error("Streaming response body is missing");
|
|
2774
2811
|
}
|
|
@@ -2776,7 +2813,7 @@ var SSEClient = class {
|
|
|
2776
2813
|
const decoder = new TextDecoder();
|
|
2777
2814
|
let buffer = "";
|
|
2778
2815
|
let rpcResponse = null;
|
|
2779
|
-
const dispatchBlock = (block) => {
|
|
2816
|
+
const dispatchBlock = async (block) => {
|
|
2780
2817
|
const lines = block.split("\n");
|
|
2781
2818
|
let eventName = "message";
|
|
2782
2819
|
const dataLines = [];
|
|
@@ -2804,6 +2841,9 @@ var SSEClient = class {
|
|
|
2804
2841
|
break;
|
|
2805
2842
|
case "connection":
|
|
2806
2843
|
this.options.onConnectionEvent?.(payload);
|
|
2844
|
+
if (options.delayConnectionEvents) {
|
|
2845
|
+
await this.sleep(CONNECTION_EVENT_INTERVAL_MS);
|
|
2846
|
+
}
|
|
2807
2847
|
break;
|
|
2808
2848
|
case "observability":
|
|
2809
2849
|
this.options.onObservabilityEvent?.(payload);
|
|
@@ -2823,18 +2863,21 @@ var SSEClient = class {
|
|
|
2823
2863
|
const separatorLength = separatorMatch[0].length;
|
|
2824
2864
|
const block = buffer.slice(0, separatorIndex);
|
|
2825
2865
|
buffer = buffer.slice(separatorIndex + separatorLength);
|
|
2826
|
-
dispatchBlock(block);
|
|
2866
|
+
await dispatchBlock(block);
|
|
2827
2867
|
separatorMatch = buffer.match(/\r?\n\r?\n/);
|
|
2828
2868
|
}
|
|
2829
2869
|
}
|
|
2830
2870
|
if (buffer.trim()) {
|
|
2831
|
-
dispatchBlock(buffer);
|
|
2871
|
+
await dispatchBlock(buffer);
|
|
2832
2872
|
}
|
|
2833
2873
|
if (!rpcResponse) {
|
|
2834
2874
|
throw new Error("Missing rpc-response event in streamed RPC result");
|
|
2835
2875
|
}
|
|
2836
2876
|
return rpcResponse;
|
|
2837
2877
|
}
|
|
2878
|
+
sleep(ms) {
|
|
2879
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2880
|
+
}
|
|
2838
2881
|
parseRpcResponse(data) {
|
|
2839
2882
|
if ("result" in data) {
|
|
2840
2883
|
return data.result;
|