@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/server/index.js
CHANGED
|
@@ -137,6 +137,14 @@ var init_redis = __esm({
|
|
|
137
137
|
var SESSION_TTL_SECONDS = 43200;
|
|
138
138
|
var STATE_EXPIRATION_MS = 10 * 60 * 1e3;
|
|
139
139
|
var TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1e3;
|
|
140
|
+
var DEFAULT_CLIENT_NAME = "MCP Assistant";
|
|
141
|
+
var DEFAULT_CLIENT_URI = "https://mcp-assistant.in";
|
|
142
|
+
var DEFAULT_LOGO_URI = "https://mcp-assistant.in/logo.svg";
|
|
143
|
+
var DEFAULT_POLICY_URI = "https://mcp-assistant.in/privacy";
|
|
144
|
+
var SOFTWARE_ID = "@mcp-ts";
|
|
145
|
+
var SOFTWARE_VERSION = "1.3.4";
|
|
146
|
+
var MCP_CLIENT_NAME = "mcp-ts-oauth-client";
|
|
147
|
+
var MCP_CLIENT_VERSION = "2.0";
|
|
140
148
|
|
|
141
149
|
// src/server/storage/redis-backend.ts
|
|
142
150
|
var firstChar = nanoid.customAlphabet(
|
|
@@ -152,6 +160,8 @@ var RedisStorageBackend = class {
|
|
|
152
160
|
this.redis = redis2;
|
|
153
161
|
__publicField(this, "DEFAULT_TTL", SESSION_TTL_SECONDS);
|
|
154
162
|
__publicField(this, "KEY_PREFIX", "mcp:session:");
|
|
163
|
+
__publicField(this, "IDENTITY_KEY_PREFIX", "mcp:identity:");
|
|
164
|
+
__publicField(this, "IDENTITY_KEY_SUFFIX", ":sessions");
|
|
155
165
|
}
|
|
156
166
|
/**
|
|
157
167
|
* Generates Redis key for a specific session
|
|
@@ -165,7 +175,34 @@ var RedisStorageBackend = class {
|
|
|
165
175
|
* @private
|
|
166
176
|
*/
|
|
167
177
|
getIdentityKey(identity) {
|
|
168
|
-
return
|
|
178
|
+
return `${this.IDENTITY_KEY_PREFIX}${identity}${this.IDENTITY_KEY_SUFFIX}`;
|
|
179
|
+
}
|
|
180
|
+
parseIdentityFromKey(identityKey) {
|
|
181
|
+
return identityKey.slice(
|
|
182
|
+
this.IDENTITY_KEY_PREFIX.length,
|
|
183
|
+
identityKey.length - this.IDENTITY_KEY_SUFFIX.length
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
async scanKeys(pattern) {
|
|
187
|
+
const redis2 = this.redis;
|
|
188
|
+
if (typeof redis2.scan !== "function") {
|
|
189
|
+
return await this.redis.keys(pattern);
|
|
190
|
+
}
|
|
191
|
+
const keys = /* @__PURE__ */ new Set();
|
|
192
|
+
let cursor = "0";
|
|
193
|
+
try {
|
|
194
|
+
do {
|
|
195
|
+
const [nextCursor, batch] = await redis2.scan(cursor, "MATCH", pattern, "COUNT", 100);
|
|
196
|
+
cursor = nextCursor;
|
|
197
|
+
for (const key of batch) {
|
|
198
|
+
keys.add(key);
|
|
199
|
+
}
|
|
200
|
+
} while (cursor !== "0");
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.warn("[RedisStorage] SCAN failed, falling back to KEYS:", error);
|
|
203
|
+
return await this.redis.keys(pattern);
|
|
204
|
+
}
|
|
205
|
+
return Array.from(keys);
|
|
169
206
|
}
|
|
170
207
|
generateSessionId() {
|
|
171
208
|
return firstChar() + rest();
|
|
@@ -233,17 +270,13 @@ var RedisStorageBackend = class {
|
|
|
233
270
|
}
|
|
234
271
|
}
|
|
235
272
|
async getIdentityMcpSessions(identity) {
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
return await this.redis.smembers(identityKey);
|
|
239
|
-
} catch (error) {
|
|
240
|
-
console.error(`[RedisStorage] Failed to get sessions for ${identity}:`, error);
|
|
241
|
-
return [];
|
|
242
|
-
}
|
|
273
|
+
const sessions = await this.getIdentitySessionsData(identity);
|
|
274
|
+
return sessions.map((session) => session.sessionId);
|
|
243
275
|
}
|
|
244
276
|
async getIdentitySessionsData(identity) {
|
|
245
277
|
try {
|
|
246
|
-
const
|
|
278
|
+
const identityKey = this.getIdentityKey(identity);
|
|
279
|
+
const sessionIds = await this.redis.smembers(identityKey);
|
|
247
280
|
if (sessionIds.length === 0) return [];
|
|
248
281
|
const results = await Promise.all(
|
|
249
282
|
sessionIds.map(async (sessionId) => {
|
|
@@ -251,6 +284,10 @@ var RedisStorageBackend = class {
|
|
|
251
284
|
return data ? JSON.parse(data) : null;
|
|
252
285
|
})
|
|
253
286
|
);
|
|
287
|
+
const staleSessionIds = sessionIds.filter((_, index) => results[index] === null);
|
|
288
|
+
if (staleSessionIds.length > 0) {
|
|
289
|
+
await this.redis.srem(identityKey, ...staleSessionIds);
|
|
290
|
+
}
|
|
254
291
|
return results.filter((session) => session !== null);
|
|
255
292
|
} catch (error) {
|
|
256
293
|
console.error(`[RedisStorage] Failed to get session data for ${identity}:`, error);
|
|
@@ -269,9 +306,22 @@ var RedisStorageBackend = class {
|
|
|
269
306
|
}
|
|
270
307
|
async getAllSessionIds() {
|
|
271
308
|
try {
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
|
|
309
|
+
const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
|
|
310
|
+
const sessions = await Promise.all(
|
|
311
|
+
keys.map(async (key) => {
|
|
312
|
+
const data = await this.redis.get(key);
|
|
313
|
+
if (!data) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
return JSON.parse(data).sessionId;
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.error("[RedisStorage] Failed to parse session while listing all session IDs:", error);
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
);
|
|
324
|
+
return sessions.filter((sessionId) => sessionId !== null);
|
|
275
325
|
} catch (error) {
|
|
276
326
|
console.error("[RedisStorage] Failed to get all sessions:", error);
|
|
277
327
|
return [];
|
|
@@ -279,10 +329,11 @@ var RedisStorageBackend = class {
|
|
|
279
329
|
}
|
|
280
330
|
async clearAll() {
|
|
281
331
|
try {
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
332
|
+
const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
|
|
333
|
+
const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
|
|
334
|
+
const allKeys = [...keys, ...identityKeys];
|
|
335
|
+
if (allKeys.length > 0) {
|
|
336
|
+
await this.redis.del(...allKeys);
|
|
286
337
|
}
|
|
287
338
|
} catch (error) {
|
|
288
339
|
console.error("[RedisStorage] Failed to clear sessions:", error);
|
|
@@ -290,12 +341,24 @@ var RedisStorageBackend = class {
|
|
|
290
341
|
}
|
|
291
342
|
async cleanupExpiredSessions() {
|
|
292
343
|
try {
|
|
293
|
-
const
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
if (
|
|
298
|
-
await this.redis.del(
|
|
344
|
+
const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
|
|
345
|
+
for (const identityKey of identityKeys) {
|
|
346
|
+
const identity = this.parseIdentityFromKey(identityKey);
|
|
347
|
+
const sessionIds = await this.redis.smembers(identityKey);
|
|
348
|
+
if (sessionIds.length === 0) {
|
|
349
|
+
await this.redis.del(identityKey);
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
const existenceChecks = await Promise.all(
|
|
353
|
+
sessionIds.map((sessionId) => this.redis.exists(this.getSessionKey(identity, sessionId)))
|
|
354
|
+
);
|
|
355
|
+
const staleSessionIds = sessionIds.filter((_, index) => existenceChecks[index] === 0);
|
|
356
|
+
if (staleSessionIds.length > 0) {
|
|
357
|
+
await this.redis.srem(identityKey, ...staleSessionIds);
|
|
358
|
+
}
|
|
359
|
+
const remainingCount = await this.redis.scard(identityKey);
|
|
360
|
+
if (remainingCount === 0) {
|
|
361
|
+
await this.redis.del(identityKey);
|
|
299
362
|
}
|
|
300
363
|
}
|
|
301
364
|
} catch (error) {
|
|
@@ -666,6 +729,12 @@ var SqliteStorage = class {
|
|
|
666
729
|
// src/server/storage/index.ts
|
|
667
730
|
var storageInstance = null;
|
|
668
731
|
var storagePromise = null;
|
|
732
|
+
async function initializeStorage(store) {
|
|
733
|
+
if (typeof store.init === "function") {
|
|
734
|
+
await store.init();
|
|
735
|
+
}
|
|
736
|
+
return store;
|
|
737
|
+
}
|
|
669
738
|
async function createStorage() {
|
|
670
739
|
const type = process.env.MCP_TS_STORAGE_TYPE?.toLowerCase();
|
|
671
740
|
if (type === "redis") {
|
|
@@ -689,16 +758,12 @@ async function createStorage() {
|
|
|
689
758
|
console.warn('[Storage] MCP_TS_STORAGE_TYPE is "file" but MCP_TS_STORAGE_FILE is missing');
|
|
690
759
|
}
|
|
691
760
|
console.log(`[Storage] Using File storage (${filePath}) (Explicit)`);
|
|
692
|
-
|
|
693
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
694
|
-
return store;
|
|
761
|
+
return await initializeStorage(new FileStorageBackend({ path: filePath }));
|
|
695
762
|
}
|
|
696
763
|
if (type === "sqlite") {
|
|
697
764
|
const dbPath = process.env.MCP_TS_STORAGE_SQLITE_PATH;
|
|
698
765
|
console.log(`[Storage] Using SQLite storage (${dbPath || "default"}) (Explicit)`);
|
|
699
|
-
|
|
700
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
701
|
-
return store;
|
|
766
|
+
return await initializeStorage(new SqliteStorage({ path: dbPath }));
|
|
702
767
|
}
|
|
703
768
|
if (type === "memory") {
|
|
704
769
|
console.log("[Storage] Using In-Memory storage (Explicit)");
|
|
@@ -718,15 +783,11 @@ async function createStorage() {
|
|
|
718
783
|
}
|
|
719
784
|
if (process.env.MCP_TS_STORAGE_FILE) {
|
|
720
785
|
console.log(`[Storage] Auto-detected MCP_TS_STORAGE_FILE. Using File storage (${process.env.MCP_TS_STORAGE_FILE}).`);
|
|
721
|
-
|
|
722
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
723
|
-
return store;
|
|
786
|
+
return await initializeStorage(new FileStorageBackend({ path: process.env.MCP_TS_STORAGE_FILE }));
|
|
724
787
|
}
|
|
725
788
|
if (process.env.MCP_TS_STORAGE_SQLITE_PATH) {
|
|
726
789
|
console.log(`[Storage] Auto-detected MCP_TS_STORAGE_SQLITE_PATH. Using SQLite storage (${process.env.MCP_TS_STORAGE_SQLITE_PATH}).`);
|
|
727
|
-
|
|
728
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
729
|
-
return store;
|
|
790
|
+
return await initializeStorage(new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH }));
|
|
730
791
|
}
|
|
731
792
|
console.log("[Storage] No storage configured. Using In-Memory storage (Default).");
|
|
732
793
|
return new MemoryStorageBackend();
|
|
@@ -736,7 +797,10 @@ async function getStorage() {
|
|
|
736
797
|
return storageInstance;
|
|
737
798
|
}
|
|
738
799
|
if (!storagePromise) {
|
|
739
|
-
storagePromise = createStorage()
|
|
800
|
+
storagePromise = createStorage().catch((error) => {
|
|
801
|
+
storagePromise = null;
|
|
802
|
+
throw error;
|
|
803
|
+
});
|
|
740
804
|
}
|
|
741
805
|
storageInstance = await storagePromise;
|
|
742
806
|
return storageInstance;
|
|
@@ -757,43 +821,49 @@ var storage = new Proxy({}, {
|
|
|
757
821
|
// src/server/mcp/storage-oauth-provider.ts
|
|
758
822
|
var StorageOAuthClientProvider = class {
|
|
759
823
|
/**
|
|
760
|
-
* Creates a new
|
|
761
|
-
* @param
|
|
762
|
-
* @param serverId - Server identifier (for tracking which server this OAuth session belongs to)
|
|
763
|
-
* @param sessionId - Session identifier (used as OAuth state)
|
|
764
|
-
* @param clientName - OAuth client name
|
|
765
|
-
* @param baseRedirectUrl - OAuth callback URL
|
|
766
|
-
* @param onRedirect - Optional callback when redirect to authorization is needed
|
|
824
|
+
* Creates a new storage-backed OAuth provider
|
|
825
|
+
* @param options - Provider configuration
|
|
767
826
|
*/
|
|
768
|
-
constructor(
|
|
769
|
-
this
|
|
770
|
-
this
|
|
771
|
-
this
|
|
772
|
-
this
|
|
773
|
-
this
|
|
827
|
+
constructor(options) {
|
|
828
|
+
__publicField(this, "identity");
|
|
829
|
+
__publicField(this, "serverId");
|
|
830
|
+
__publicField(this, "sessionId");
|
|
831
|
+
__publicField(this, "redirectUrl");
|
|
832
|
+
__publicField(this, "clientName");
|
|
833
|
+
__publicField(this, "clientUri");
|
|
834
|
+
__publicField(this, "logoUri");
|
|
835
|
+
__publicField(this, "policyUri");
|
|
836
|
+
__publicField(this, "clientSecret");
|
|
774
837
|
__publicField(this, "_authUrl");
|
|
775
838
|
__publicField(this, "_clientId");
|
|
776
839
|
__publicField(this, "onRedirectCallback");
|
|
777
840
|
__publicField(this, "tokenExpiresAt");
|
|
778
|
-
this.
|
|
841
|
+
this.identity = options.identity;
|
|
842
|
+
this.serverId = options.serverId;
|
|
843
|
+
this.sessionId = options.sessionId;
|
|
844
|
+
this.redirectUrl = options.redirectUrl;
|
|
845
|
+
this.clientName = options.clientName;
|
|
846
|
+
this.clientUri = options.clientUri;
|
|
847
|
+
this.logoUri = options.logoUri;
|
|
848
|
+
this.policyUri = options.policyUri;
|
|
849
|
+
this._clientId = options.clientId;
|
|
850
|
+
this.clientSecret = options.clientSecret;
|
|
851
|
+
this.onRedirectCallback = options.onRedirect;
|
|
779
852
|
}
|
|
780
853
|
get clientMetadata() {
|
|
781
854
|
return {
|
|
782
|
-
client_name: this.clientName,
|
|
783
|
-
client_uri: this.clientUri,
|
|
855
|
+
client_name: this.clientName || DEFAULT_CLIENT_NAME,
|
|
856
|
+
client_uri: this.clientUri || DEFAULT_CLIENT_URI,
|
|
857
|
+
logo_uri: this.logoUri || DEFAULT_LOGO_URI,
|
|
858
|
+
policy_uri: this.policyUri || DEFAULT_POLICY_URI,
|
|
784
859
|
grant_types: ["authorization_code", "refresh_token"],
|
|
785
860
|
redirect_uris: [this.redirectUrl],
|
|
786
861
|
response_types: ["code"],
|
|
787
|
-
token_endpoint_auth_method: "none",
|
|
788
|
-
|
|
862
|
+
token_endpoint_auth_method: this.clientSecret ? "client_secret_basic" : "none",
|
|
863
|
+
software_id: SOFTWARE_ID,
|
|
864
|
+
software_version: SOFTWARE_VERSION
|
|
789
865
|
};
|
|
790
866
|
}
|
|
791
|
-
get clientUri() {
|
|
792
|
-
return new URL(this.redirectUrl).origin;
|
|
793
|
-
}
|
|
794
|
-
get redirectUrl() {
|
|
795
|
-
return this.baseRedirectUrl;
|
|
796
|
-
}
|
|
797
867
|
get clientId() {
|
|
798
868
|
return this._clientId;
|
|
799
869
|
}
|
|
@@ -828,7 +898,16 @@ var StorageOAuthClientProvider = class {
|
|
|
828
898
|
if (data.clientId && !this._clientId) {
|
|
829
899
|
this._clientId = data.clientId;
|
|
830
900
|
}
|
|
831
|
-
|
|
901
|
+
if (data.clientInformation) {
|
|
902
|
+
return data.clientInformation;
|
|
903
|
+
}
|
|
904
|
+
if (!this._clientId) {
|
|
905
|
+
return void 0;
|
|
906
|
+
}
|
|
907
|
+
return {
|
|
908
|
+
client_id: this._clientId,
|
|
909
|
+
...this.clientSecret ? { client_secret: this.clientSecret } : {}
|
|
910
|
+
};
|
|
832
911
|
}
|
|
833
912
|
/**
|
|
834
913
|
* Stores OAuth client information
|
|
@@ -856,14 +935,14 @@ var StorageOAuthClientProvider = class {
|
|
|
856
935
|
async state() {
|
|
857
936
|
return this.sessionId;
|
|
858
937
|
}
|
|
859
|
-
async checkState(
|
|
938
|
+
async checkState(_state) {
|
|
860
939
|
const data = await storage.getSession(this.identity, this.sessionId);
|
|
861
940
|
if (!data) {
|
|
862
941
|
return { valid: false, error: "Session not found" };
|
|
863
942
|
}
|
|
864
943
|
return { valid: true, serverId: this.serverId };
|
|
865
944
|
}
|
|
866
|
-
async consumeState(
|
|
945
|
+
async consumeState(_state) {
|
|
867
946
|
}
|
|
868
947
|
async redirectToAuthorization(authUrl) {
|
|
869
948
|
this._authUrl = authUrl.toString();
|
|
@@ -875,7 +954,6 @@ var StorageOAuthClientProvider = class {
|
|
|
875
954
|
if (scope === "all") {
|
|
876
955
|
await storage.removeSession(this.identity, this.sessionId);
|
|
877
956
|
} else {
|
|
878
|
-
await this.getSessionData();
|
|
879
957
|
const updates = {};
|
|
880
958
|
if (scope === "client") {
|
|
881
959
|
updates.clientInformation = void 0;
|
|
@@ -1195,41 +1273,33 @@ var MCPClient = class _MCPClient {
|
|
|
1195
1273
|
if (!this.serverUrl || !this.callbackUrl || !this.serverId) {
|
|
1196
1274
|
throw new Error("Missing required connection metadata");
|
|
1197
1275
|
}
|
|
1198
|
-
const clientMetadata = {
|
|
1199
|
-
client_name: this.clientName || "MCP Assistant",
|
|
1200
|
-
redirect_uris: [this.callbackUrl],
|
|
1201
|
-
token_endpoint_auth_method: this.clientSecret ? "client_secret_basic" : "none",
|
|
1202
|
-
client_uri: this.clientUri || "https://mcp-assistant.in",
|
|
1203
|
-
logo_uri: this.logoUri || "https://mcp-assistant.in/logo.png",
|
|
1204
|
-
policy_uri: this.policyUri || "https://mcp-assistant.in/privacy",
|
|
1205
|
-
...this.clientId ? { client_id: this.clientId } : {},
|
|
1206
|
-
...this.clientSecret ? { client_secret: this.clientSecret } : {}
|
|
1207
|
-
};
|
|
1208
1276
|
if (!this.oauthProvider) {
|
|
1209
1277
|
if (!this.serverId) {
|
|
1210
1278
|
throw new Error("serverId required for OAuth provider initialization");
|
|
1211
1279
|
}
|
|
1212
|
-
this.oauthProvider = new StorageOAuthClientProvider(
|
|
1213
|
-
this.identity,
|
|
1214
|
-
this.serverId,
|
|
1215
|
-
this.sessionId,
|
|
1216
|
-
|
|
1217
|
-
this.
|
|
1218
|
-
|
|
1280
|
+
this.oauthProvider = new StorageOAuthClientProvider({
|
|
1281
|
+
identity: this.identity,
|
|
1282
|
+
serverId: this.serverId,
|
|
1283
|
+
sessionId: this.sessionId,
|
|
1284
|
+
redirectUrl: this.callbackUrl,
|
|
1285
|
+
clientName: this.clientName,
|
|
1286
|
+
clientUri: this.clientUri,
|
|
1287
|
+
logoUri: this.logoUri,
|
|
1288
|
+
policyUri: this.policyUri,
|
|
1289
|
+
clientId: this.clientId,
|
|
1290
|
+
clientSecret: this.clientSecret,
|
|
1291
|
+
onRedirect: (redirectUrl) => {
|
|
1219
1292
|
if (this.onRedirect) {
|
|
1220
1293
|
this.onRedirect(redirectUrl);
|
|
1221
1294
|
}
|
|
1222
1295
|
}
|
|
1223
|
-
);
|
|
1224
|
-
if (this.clientId && this.oauthProvider) {
|
|
1225
|
-
this.oauthProvider.clientId = this.clientId;
|
|
1226
|
-
}
|
|
1296
|
+
});
|
|
1227
1297
|
}
|
|
1228
1298
|
if (!this.client) {
|
|
1229
1299
|
this.client = new index_js.Client(
|
|
1230
1300
|
{
|
|
1231
|
-
name:
|
|
1232
|
-
version:
|
|
1301
|
+
name: MCP_CLIENT_NAME,
|
|
1302
|
+
version: MCP_CLIENT_VERSION
|
|
1233
1303
|
},
|
|
1234
1304
|
{
|
|
1235
1305
|
capabilities: {
|
|
@@ -1424,8 +1494,8 @@ var MCPClient = class _MCPClient {
|
|
|
1424
1494
|
this.emitProgress("Creating authenticated client...");
|
|
1425
1495
|
this.client = new index_js.Client(
|
|
1426
1496
|
{
|
|
1427
|
-
name:
|
|
1428
|
-
version:
|
|
1497
|
+
name: MCP_CLIENT_NAME,
|
|
1498
|
+
version: MCP_CLIENT_VERSION
|
|
1429
1499
|
},
|
|
1430
1500
|
{
|
|
1431
1501
|
capabilities: {
|
|
@@ -1720,8 +1790,8 @@ var MCPClient = class _MCPClient {
|
|
|
1720
1790
|
}
|
|
1721
1791
|
this.client = new index_js.Client(
|
|
1722
1792
|
{
|
|
1723
|
-
name:
|
|
1724
|
-
version:
|
|
1793
|
+
name: MCP_CLIENT_NAME,
|
|
1794
|
+
version: MCP_CLIENT_VERSION
|
|
1725
1795
|
},
|
|
1726
1796
|
{ capabilities: {} }
|
|
1727
1797
|
);
|
|
@@ -1990,6 +2060,27 @@ var MultiSessionClient = class {
|
|
|
1990
2060
|
}
|
|
1991
2061
|
};
|
|
1992
2062
|
|
|
2063
|
+
// src/shared/event-routing.ts
|
|
2064
|
+
function isRpcResponseEvent(event) {
|
|
2065
|
+
return "id" in event && ("result" in event || "error" in event);
|
|
2066
|
+
}
|
|
2067
|
+
function isConnectionEvent(event) {
|
|
2068
|
+
if (!("type" in event)) {
|
|
2069
|
+
return false;
|
|
2070
|
+
}
|
|
2071
|
+
switch (event.type) {
|
|
2072
|
+
case "state_changed":
|
|
2073
|
+
case "tools_discovered":
|
|
2074
|
+
case "auth_required":
|
|
2075
|
+
case "error":
|
|
2076
|
+
case "disconnected":
|
|
2077
|
+
case "progress":
|
|
2078
|
+
return true;
|
|
2079
|
+
default:
|
|
2080
|
+
return false;
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
|
|
1993
2084
|
// src/server/handlers/sse-handler.ts
|
|
1994
2085
|
var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
|
|
1995
2086
|
var SSEConnectionManager = class {
|
|
@@ -2132,16 +2223,6 @@ var SSEConnectionManager = class {
|
|
|
2132
2223
|
throw new Error(`Connection already exists for server: ${duplicate.serverUrl || duplicate.serverId} (${duplicate.serverName})`);
|
|
2133
2224
|
}
|
|
2134
2225
|
const sessionId = await storage.generateSessionId();
|
|
2135
|
-
this.emitConnectionEvent({
|
|
2136
|
-
type: "state_changed",
|
|
2137
|
-
sessionId,
|
|
2138
|
-
serverId,
|
|
2139
|
-
serverName,
|
|
2140
|
-
serverUrl,
|
|
2141
|
-
state: "CONNECTING",
|
|
2142
|
-
previousState: "DISCONNECTED",
|
|
2143
|
-
timestamp: Date.now()
|
|
2144
|
-
});
|
|
2145
2226
|
try {
|
|
2146
2227
|
const clientMetadata = await this.getResolvedClientMetadata();
|
|
2147
2228
|
const client = new MCPClient({
|
|
@@ -2152,17 +2233,8 @@ var SSEConnectionManager = class {
|
|
|
2152
2233
|
serverUrl,
|
|
2153
2234
|
callbackUrl,
|
|
2154
2235
|
transportType,
|
|
2155
|
-
...clientMetadata
|
|
2236
|
+
...clientMetadata
|
|
2156
2237
|
// Spread client metadata (clientName, clientUri, logoUri, policyUri)
|
|
2157
|
-
onRedirect: (authUrl) => {
|
|
2158
|
-
this.emitConnectionEvent({
|
|
2159
|
-
type: "auth_required",
|
|
2160
|
-
sessionId,
|
|
2161
|
-
serverId,
|
|
2162
|
-
authUrl,
|
|
2163
|
-
timestamp: Date.now()
|
|
2164
|
-
});
|
|
2165
|
-
}
|
|
2166
2238
|
});
|
|
2167
2239
|
this.clients.set(sessionId, client);
|
|
2168
2240
|
client.onConnectionEvent((event) => {
|
|
@@ -2172,20 +2244,19 @@ var SSEConnectionManager = class {
|
|
|
2172
2244
|
this.sendEvent(event);
|
|
2173
2245
|
});
|
|
2174
2246
|
await client.connect();
|
|
2175
|
-
|
|
2176
|
-
this.emitConnectionEvent({
|
|
2177
|
-
type: "tools_discovered",
|
|
2178
|
-
sessionId,
|
|
2179
|
-
serverId,
|
|
2180
|
-
toolCount: tools.tools.length,
|
|
2181
|
-
tools: tools.tools,
|
|
2182
|
-
timestamp: Date.now()
|
|
2183
|
-
});
|
|
2247
|
+
await client.listTools();
|
|
2184
2248
|
return {
|
|
2185
2249
|
sessionId,
|
|
2186
2250
|
success: true
|
|
2187
2251
|
};
|
|
2188
2252
|
} catch (error) {
|
|
2253
|
+
if (error instanceof UnauthorizedError) {
|
|
2254
|
+
this.clients.delete(sessionId);
|
|
2255
|
+
return {
|
|
2256
|
+
sessionId,
|
|
2257
|
+
success: true
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2189
2260
|
this.emitConnectionEvent({
|
|
2190
2261
|
type: "error",
|
|
2191
2262
|
sessionId,
|
|
@@ -2287,14 +2358,6 @@ var SSEConnectionManager = class {
|
|
|
2287
2358
|
await client.connect();
|
|
2288
2359
|
this.clients.set(sessionId, client);
|
|
2289
2360
|
const tools = await client.listTools();
|
|
2290
|
-
this.emitConnectionEvent({
|
|
2291
|
-
type: "tools_discovered",
|
|
2292
|
-
sessionId,
|
|
2293
|
-
serverId: session.serverId ?? "unknown",
|
|
2294
|
-
toolCount: tools.tools.length,
|
|
2295
|
-
tools: tools.tools,
|
|
2296
|
-
timestamp: Date.now()
|
|
2297
|
-
});
|
|
2298
2361
|
return { success: true, toolCount: tools.tools.length };
|
|
2299
2362
|
} catch (error) {
|
|
2300
2363
|
this.emitConnectionEvent({
|
|
@@ -2317,16 +2380,6 @@ var SSEConnectionManager = class {
|
|
|
2317
2380
|
if (!session) {
|
|
2318
2381
|
throw new Error("Session not found");
|
|
2319
2382
|
}
|
|
2320
|
-
this.emitConnectionEvent({
|
|
2321
|
-
type: "state_changed",
|
|
2322
|
-
sessionId,
|
|
2323
|
-
serverId: session.serverId ?? "unknown",
|
|
2324
|
-
serverName: session.serverName ?? "Unknown",
|
|
2325
|
-
serverUrl: session.serverUrl,
|
|
2326
|
-
state: "AUTHENTICATING",
|
|
2327
|
-
previousState: "DISCONNECTED",
|
|
2328
|
-
timestamp: Date.now()
|
|
2329
|
-
});
|
|
2330
2383
|
try {
|
|
2331
2384
|
const client = new MCPClient({
|
|
2332
2385
|
identity: this.identity,
|
|
@@ -2336,14 +2389,6 @@ var SSEConnectionManager = class {
|
|
|
2336
2389
|
await client.finishAuth(code);
|
|
2337
2390
|
this.clients.set(sessionId, client);
|
|
2338
2391
|
const tools = await client.listTools();
|
|
2339
|
-
this.emitConnectionEvent({
|
|
2340
|
-
type: "tools_discovered",
|
|
2341
|
-
sessionId,
|
|
2342
|
-
serverId: session.serverId ?? "unknown",
|
|
2343
|
-
toolCount: tools.tools.length,
|
|
2344
|
-
tools: tools.tools,
|
|
2345
|
-
timestamp: Date.now()
|
|
2346
|
-
});
|
|
2347
2392
|
return { success: true, toolCount: tools.tools.length };
|
|
2348
2393
|
} catch (error) {
|
|
2349
2394
|
this.emitConnectionEvent({
|
|
@@ -2421,9 +2466,9 @@ function createSSEHandler(options) {
|
|
|
2421
2466
|
});
|
|
2422
2467
|
writeSSEEvent(res, "connected", { timestamp: Date.now() });
|
|
2423
2468
|
const manager = new SSEConnectionManager(options, (event) => {
|
|
2424
|
-
if (
|
|
2469
|
+
if (isRpcResponseEvent(event)) {
|
|
2425
2470
|
writeSSEEvent(res, "rpc-response", event);
|
|
2426
|
-
} else if (
|
|
2471
|
+
} else if (isConnectionEvent(event)) {
|
|
2427
2472
|
writeSSEEvent(res, "connection", event);
|
|
2428
2473
|
} else {
|
|
2429
2474
|
writeSSEEvent(res, "observability", event);
|
|
@@ -2454,9 +2499,6 @@ function writeSSEEvent(res, event, data) {
|
|
|
2454
2499
|
}
|
|
2455
2500
|
|
|
2456
2501
|
// src/server/handlers/nextjs-handler.ts
|
|
2457
|
-
function isRpcResponseEvent(event) {
|
|
2458
|
-
return "id" in event && ("result" in event || "error" in event);
|
|
2459
|
-
}
|
|
2460
2502
|
function createNextMcpHandler(options = {}) {
|
|
2461
2503
|
const {
|
|
2462
2504
|
getIdentity = (request) => new URL(request.url).searchParams.get("identity"),
|
|
@@ -2547,7 +2589,7 @@ data: ${JSON.stringify(data)}
|
|
|
2547
2589
|
(event) => {
|
|
2548
2590
|
if (isRpcResponseEvent(event)) {
|
|
2549
2591
|
sendSSE("rpc-response", event);
|
|
2550
|
-
} else if (
|
|
2592
|
+
} else if (isConnectionEvent(event)) {
|
|
2551
2593
|
sendSSE("connection", event);
|
|
2552
2594
|
} else {
|
|
2553
2595
|
sendSSE("observability", event);
|