@mcp-ts/sdk 1.3.4 → 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.
Files changed (58) hide show
  1. package/README.md +404 -400
  2. package/dist/adapters/agui-adapter.d.mts +1 -1
  3. package/dist/adapters/agui-adapter.d.ts +1 -1
  4. package/dist/adapters/agui-middleware.d.mts +1 -1
  5. package/dist/adapters/agui-middleware.d.ts +1 -1
  6. package/dist/adapters/ai-adapter.d.mts +1 -1
  7. package/dist/adapters/ai-adapter.d.ts +1 -1
  8. package/dist/adapters/langchain-adapter.d.mts +1 -1
  9. package/dist/adapters/langchain-adapter.d.ts +1 -1
  10. package/dist/adapters/mastra-adapter.d.mts +1 -1
  11. package/dist/adapters/mastra-adapter.d.ts +1 -1
  12. package/dist/client/index.d.mts +1 -0
  13. package/dist/client/index.d.ts +1 -0
  14. package/dist/client/index.js +14 -5
  15. package/dist/client/index.js.map +1 -1
  16. package/dist/client/index.mjs +14 -5
  17. package/dist/client/index.mjs.map +1 -1
  18. package/dist/client/react.js +15 -6
  19. package/dist/client/react.js.map +1 -1
  20. package/dist/client/react.mjs +15 -6
  21. package/dist/client/react.mjs.map +1 -1
  22. package/dist/client/vue.js +15 -6
  23. package/dist/client/vue.js.map +1 -1
  24. package/dist/client/vue.mjs +15 -6
  25. package/dist/client/vue.mjs.map +1 -1
  26. package/dist/index.d.mts +1 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +201 -158
  29. package/dist/index.js.map +1 -1
  30. package/dist/index.mjs +201 -158
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/{multi-session-client-FAFpUzZ4.d.ts → multi-session-client-BYLarghq.d.ts} +29 -19
  33. package/dist/{multi-session-client-DzjmT7FX.d.mts → multi-session-client-CzhMkE0k.d.mts} +29 -19
  34. package/dist/server/index.d.mts +1 -1
  35. package/dist/server/index.d.ts +1 -1
  36. package/dist/server/index.js +193 -151
  37. package/dist/server/index.js.map +1 -1
  38. package/dist/server/index.mjs +193 -151
  39. package/dist/server/index.mjs.map +1 -1
  40. package/dist/shared/index.d.mts +2 -2
  41. package/dist/shared/index.d.ts +2 -2
  42. package/dist/shared/index.js +2 -2
  43. package/dist/shared/index.js.map +1 -1
  44. package/dist/shared/index.mjs +2 -2
  45. package/dist/shared/index.mjs.map +1 -1
  46. package/package.json +1 -1
  47. package/src/client/core/sse-client.ts +371 -354
  48. package/src/client/react/use-mcp.ts +31 -31
  49. package/src/client/vue/use-mcp.ts +77 -77
  50. package/src/server/handlers/nextjs-handler.ts +194 -197
  51. package/src/server/handlers/sse-handler.ts +62 -111
  52. package/src/server/mcp/oauth-client.ts +67 -79
  53. package/src/server/mcp/storage-oauth-provider.ts +71 -38
  54. package/src/server/storage/index.ts +15 -13
  55. package/src/server/storage/redis-backend.ts +93 -23
  56. package/src/server/storage/types.ts +12 -12
  57. package/src/shared/constants.ts +2 -2
  58. 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.png";
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.0.0-beta.5";
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 `mcp:identity:${identity}:sessions`;
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 identityKey = this.getIdentityKey(identity);
226
- try {
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 sessionIds = await this.redis.smembers(this.getIdentityKey(identity));
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 pattern = `${this.KEY_PREFIX}*`;
262
- const keys = await this.redis.keys(pattern);
263
- return keys.map((key) => key.replace(this.KEY_PREFIX, ""));
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 pattern = `${this.KEY_PREFIX}*`;
272
- const keys = await this.redis.keys(pattern);
273
- if (keys.length > 0) {
274
- await this.redis.del(...keys);
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 pattern = `${this.KEY_PREFIX}*`;
283
- const keys = await this.redis.keys(pattern);
284
- for (const key of keys) {
285
- const ttl = await this.redis.ttl(key);
286
- if (ttl <= 0) {
287
- await this.redis.del(key);
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
- const store = new FileStorageBackend({ path: filePath });
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
- const store = new SqliteStorage({ path: dbPath });
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
- const store = new FileStorageBackend({ path: process.env.MCP_TS_STORAGE_FILE });
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
- const store = new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH });
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 Storage-backed OAuth provider
750
- * @param identity - User/Client identifier
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(identity, serverId, sessionId, clientName, baseRedirectUrl, onRedirect) {
758
- this.identity = identity;
759
- this.serverId = serverId;
760
- this.sessionId = sessionId;
761
- this.clientName = clientName;
762
- this.baseRedirectUrl = baseRedirectUrl;
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.onRedirectCallback = onRedirect;
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
- ...this._clientId ? { client_id: this._clientId } : {}
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
- return data.clientInformation;
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(state) {
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(state) {
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
- clientMetadata.client_name ?? "MCP Assistant",
1274
- this.callbackUrl,
1275
- (redirectUrl) => {
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: "mcp-ts-oauth-client",
1289
- version: "2.0"
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: "mcp-ts-oauth-client",
1485
- version: "2.0"
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: "mcp-ts-oauth-client",
1781
- version: "2.0"
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
- const tools = await client.listTools();
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 ("id" in event) {
2518
+ if (isRpcResponseEvent(event)) {
2482
2519
  writeSSEEvent(res, "rpc-response", event);
2483
- } else if ("type" in event && "sessionId" in event) {
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 ("type" in event && "sessionId" in event) {
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;