@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.mjs
CHANGED
|
@@ -115,6 +115,14 @@ var init_redis = __esm({
|
|
|
115
115
|
var SESSION_TTL_SECONDS = 43200;
|
|
116
116
|
var STATE_EXPIRATION_MS = 10 * 60 * 1e3;
|
|
117
117
|
var TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1e3;
|
|
118
|
+
var DEFAULT_CLIENT_NAME = "MCP Assistant";
|
|
119
|
+
var DEFAULT_CLIENT_URI = "https://mcp-assistant.in";
|
|
120
|
+
var DEFAULT_LOGO_URI = "https://mcp-assistant.in/logo.svg";
|
|
121
|
+
var DEFAULT_POLICY_URI = "https://mcp-assistant.in/privacy";
|
|
122
|
+
var SOFTWARE_ID = "@mcp-ts";
|
|
123
|
+
var SOFTWARE_VERSION = "1.3.4";
|
|
124
|
+
var MCP_CLIENT_NAME = "mcp-ts-oauth-client";
|
|
125
|
+
var MCP_CLIENT_VERSION = "2.0";
|
|
118
126
|
|
|
119
127
|
// src/server/storage/redis-backend.ts
|
|
120
128
|
var firstChar = customAlphabet(
|
|
@@ -130,6 +138,8 @@ var RedisStorageBackend = class {
|
|
|
130
138
|
this.redis = redis2;
|
|
131
139
|
__publicField(this, "DEFAULT_TTL", SESSION_TTL_SECONDS);
|
|
132
140
|
__publicField(this, "KEY_PREFIX", "mcp:session:");
|
|
141
|
+
__publicField(this, "IDENTITY_KEY_PREFIX", "mcp:identity:");
|
|
142
|
+
__publicField(this, "IDENTITY_KEY_SUFFIX", ":sessions");
|
|
133
143
|
}
|
|
134
144
|
/**
|
|
135
145
|
* Generates Redis key for a specific session
|
|
@@ -143,7 +153,34 @@ var RedisStorageBackend = class {
|
|
|
143
153
|
* @private
|
|
144
154
|
*/
|
|
145
155
|
getIdentityKey(identity) {
|
|
146
|
-
return
|
|
156
|
+
return `${this.IDENTITY_KEY_PREFIX}${identity}${this.IDENTITY_KEY_SUFFIX}`;
|
|
157
|
+
}
|
|
158
|
+
parseIdentityFromKey(identityKey) {
|
|
159
|
+
return identityKey.slice(
|
|
160
|
+
this.IDENTITY_KEY_PREFIX.length,
|
|
161
|
+
identityKey.length - this.IDENTITY_KEY_SUFFIX.length
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
async scanKeys(pattern) {
|
|
165
|
+
const redis2 = this.redis;
|
|
166
|
+
if (typeof redis2.scan !== "function") {
|
|
167
|
+
return await this.redis.keys(pattern);
|
|
168
|
+
}
|
|
169
|
+
const keys = /* @__PURE__ */ new Set();
|
|
170
|
+
let cursor = "0";
|
|
171
|
+
try {
|
|
172
|
+
do {
|
|
173
|
+
const [nextCursor, batch] = await redis2.scan(cursor, "MATCH", pattern, "COUNT", 100);
|
|
174
|
+
cursor = nextCursor;
|
|
175
|
+
for (const key of batch) {
|
|
176
|
+
keys.add(key);
|
|
177
|
+
}
|
|
178
|
+
} while (cursor !== "0");
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.warn("[RedisStorage] SCAN failed, falling back to KEYS:", error);
|
|
181
|
+
return await this.redis.keys(pattern);
|
|
182
|
+
}
|
|
183
|
+
return Array.from(keys);
|
|
147
184
|
}
|
|
148
185
|
generateSessionId() {
|
|
149
186
|
return firstChar() + rest();
|
|
@@ -211,17 +248,13 @@ var RedisStorageBackend = class {
|
|
|
211
248
|
}
|
|
212
249
|
}
|
|
213
250
|
async getIdentityMcpSessions(identity) {
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
return await this.redis.smembers(identityKey);
|
|
217
|
-
} catch (error) {
|
|
218
|
-
console.error(`[RedisStorage] Failed to get sessions for ${identity}:`, error);
|
|
219
|
-
return [];
|
|
220
|
-
}
|
|
251
|
+
const sessions = await this.getIdentitySessionsData(identity);
|
|
252
|
+
return sessions.map((session) => session.sessionId);
|
|
221
253
|
}
|
|
222
254
|
async getIdentitySessionsData(identity) {
|
|
223
255
|
try {
|
|
224
|
-
const
|
|
256
|
+
const identityKey = this.getIdentityKey(identity);
|
|
257
|
+
const sessionIds = await this.redis.smembers(identityKey);
|
|
225
258
|
if (sessionIds.length === 0) return [];
|
|
226
259
|
const results = await Promise.all(
|
|
227
260
|
sessionIds.map(async (sessionId) => {
|
|
@@ -229,6 +262,10 @@ var RedisStorageBackend = class {
|
|
|
229
262
|
return data ? JSON.parse(data) : null;
|
|
230
263
|
})
|
|
231
264
|
);
|
|
265
|
+
const staleSessionIds = sessionIds.filter((_, index) => results[index] === null);
|
|
266
|
+
if (staleSessionIds.length > 0) {
|
|
267
|
+
await this.redis.srem(identityKey, ...staleSessionIds);
|
|
268
|
+
}
|
|
232
269
|
return results.filter((session) => session !== null);
|
|
233
270
|
} catch (error) {
|
|
234
271
|
console.error(`[RedisStorage] Failed to get session data for ${identity}:`, error);
|
|
@@ -247,9 +284,22 @@ var RedisStorageBackend = class {
|
|
|
247
284
|
}
|
|
248
285
|
async getAllSessionIds() {
|
|
249
286
|
try {
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
|
|
287
|
+
const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
|
|
288
|
+
const sessions = await Promise.all(
|
|
289
|
+
keys.map(async (key) => {
|
|
290
|
+
const data = await this.redis.get(key);
|
|
291
|
+
if (!data) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
try {
|
|
295
|
+
return JSON.parse(data).sessionId;
|
|
296
|
+
} catch (error) {
|
|
297
|
+
console.error("[RedisStorage] Failed to parse session while listing all session IDs:", error);
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
})
|
|
301
|
+
);
|
|
302
|
+
return sessions.filter((sessionId) => sessionId !== null);
|
|
253
303
|
} catch (error) {
|
|
254
304
|
console.error("[RedisStorage] Failed to get all sessions:", error);
|
|
255
305
|
return [];
|
|
@@ -257,10 +307,11 @@ var RedisStorageBackend = class {
|
|
|
257
307
|
}
|
|
258
308
|
async clearAll() {
|
|
259
309
|
try {
|
|
260
|
-
const
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
310
|
+
const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
|
|
311
|
+
const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
|
|
312
|
+
const allKeys = [...keys, ...identityKeys];
|
|
313
|
+
if (allKeys.length > 0) {
|
|
314
|
+
await this.redis.del(...allKeys);
|
|
264
315
|
}
|
|
265
316
|
} catch (error) {
|
|
266
317
|
console.error("[RedisStorage] Failed to clear sessions:", error);
|
|
@@ -268,12 +319,24 @@ var RedisStorageBackend = class {
|
|
|
268
319
|
}
|
|
269
320
|
async cleanupExpiredSessions() {
|
|
270
321
|
try {
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
276
|
-
await this.redis.del(
|
|
322
|
+
const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
|
|
323
|
+
for (const identityKey of identityKeys) {
|
|
324
|
+
const identity = this.parseIdentityFromKey(identityKey);
|
|
325
|
+
const sessionIds = await this.redis.smembers(identityKey);
|
|
326
|
+
if (sessionIds.length === 0) {
|
|
327
|
+
await this.redis.del(identityKey);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const existenceChecks = await Promise.all(
|
|
331
|
+
sessionIds.map((sessionId) => this.redis.exists(this.getSessionKey(identity, sessionId)))
|
|
332
|
+
);
|
|
333
|
+
const staleSessionIds = sessionIds.filter((_, index) => existenceChecks[index] === 0);
|
|
334
|
+
if (staleSessionIds.length > 0) {
|
|
335
|
+
await this.redis.srem(identityKey, ...staleSessionIds);
|
|
336
|
+
}
|
|
337
|
+
const remainingCount = await this.redis.scard(identityKey);
|
|
338
|
+
if (remainingCount === 0) {
|
|
339
|
+
await this.redis.del(identityKey);
|
|
277
340
|
}
|
|
278
341
|
}
|
|
279
342
|
} catch (error) {
|
|
@@ -644,6 +707,12 @@ var SqliteStorage = class {
|
|
|
644
707
|
// src/server/storage/index.ts
|
|
645
708
|
var storageInstance = null;
|
|
646
709
|
var storagePromise = null;
|
|
710
|
+
async function initializeStorage(store) {
|
|
711
|
+
if (typeof store.init === "function") {
|
|
712
|
+
await store.init();
|
|
713
|
+
}
|
|
714
|
+
return store;
|
|
715
|
+
}
|
|
647
716
|
async function createStorage() {
|
|
648
717
|
const type = process.env.MCP_TS_STORAGE_TYPE?.toLowerCase();
|
|
649
718
|
if (type === "redis") {
|
|
@@ -667,16 +736,12 @@ async function createStorage() {
|
|
|
667
736
|
console.warn('[Storage] MCP_TS_STORAGE_TYPE is "file" but MCP_TS_STORAGE_FILE is missing');
|
|
668
737
|
}
|
|
669
738
|
console.log(`[Storage] Using File storage (${filePath}) (Explicit)`);
|
|
670
|
-
|
|
671
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
672
|
-
return store;
|
|
739
|
+
return await initializeStorage(new FileStorageBackend({ path: filePath }));
|
|
673
740
|
}
|
|
674
741
|
if (type === "sqlite") {
|
|
675
742
|
const dbPath = process.env.MCP_TS_STORAGE_SQLITE_PATH;
|
|
676
743
|
console.log(`[Storage] Using SQLite storage (${dbPath || "default"}) (Explicit)`);
|
|
677
|
-
|
|
678
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
679
|
-
return store;
|
|
744
|
+
return await initializeStorage(new SqliteStorage({ path: dbPath }));
|
|
680
745
|
}
|
|
681
746
|
if (type === "memory") {
|
|
682
747
|
console.log("[Storage] Using In-Memory storage (Explicit)");
|
|
@@ -696,15 +761,11 @@ async function createStorage() {
|
|
|
696
761
|
}
|
|
697
762
|
if (process.env.MCP_TS_STORAGE_FILE) {
|
|
698
763
|
console.log(`[Storage] Auto-detected MCP_TS_STORAGE_FILE. Using File storage (${process.env.MCP_TS_STORAGE_FILE}).`);
|
|
699
|
-
|
|
700
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
701
|
-
return store;
|
|
764
|
+
return await initializeStorage(new FileStorageBackend({ path: process.env.MCP_TS_STORAGE_FILE }));
|
|
702
765
|
}
|
|
703
766
|
if (process.env.MCP_TS_STORAGE_SQLITE_PATH) {
|
|
704
767
|
console.log(`[Storage] Auto-detected MCP_TS_STORAGE_SQLITE_PATH. Using SQLite storage (${process.env.MCP_TS_STORAGE_SQLITE_PATH}).`);
|
|
705
|
-
|
|
706
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
707
|
-
return store;
|
|
768
|
+
return await initializeStorage(new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH }));
|
|
708
769
|
}
|
|
709
770
|
console.log("[Storage] No storage configured. Using In-Memory storage (Default).");
|
|
710
771
|
return new MemoryStorageBackend();
|
|
@@ -714,7 +775,10 @@ async function getStorage() {
|
|
|
714
775
|
return storageInstance;
|
|
715
776
|
}
|
|
716
777
|
if (!storagePromise) {
|
|
717
|
-
storagePromise = createStorage()
|
|
778
|
+
storagePromise = createStorage().catch((error) => {
|
|
779
|
+
storagePromise = null;
|
|
780
|
+
throw error;
|
|
781
|
+
});
|
|
718
782
|
}
|
|
719
783
|
storageInstance = await storagePromise;
|
|
720
784
|
return storageInstance;
|
|
@@ -735,43 +799,49 @@ var storage = new Proxy({}, {
|
|
|
735
799
|
// src/server/mcp/storage-oauth-provider.ts
|
|
736
800
|
var StorageOAuthClientProvider = class {
|
|
737
801
|
/**
|
|
738
|
-
* Creates a new
|
|
739
|
-
* @param
|
|
740
|
-
* @param serverId - Server identifier (for tracking which server this OAuth session belongs to)
|
|
741
|
-
* @param sessionId - Session identifier (used as OAuth state)
|
|
742
|
-
* @param clientName - OAuth client name
|
|
743
|
-
* @param baseRedirectUrl - OAuth callback URL
|
|
744
|
-
* @param onRedirect - Optional callback when redirect to authorization is needed
|
|
802
|
+
* Creates a new storage-backed OAuth provider
|
|
803
|
+
* @param options - Provider configuration
|
|
745
804
|
*/
|
|
746
|
-
constructor(
|
|
747
|
-
this
|
|
748
|
-
this
|
|
749
|
-
this
|
|
750
|
-
this
|
|
751
|
-
this
|
|
805
|
+
constructor(options) {
|
|
806
|
+
__publicField(this, "identity");
|
|
807
|
+
__publicField(this, "serverId");
|
|
808
|
+
__publicField(this, "sessionId");
|
|
809
|
+
__publicField(this, "redirectUrl");
|
|
810
|
+
__publicField(this, "clientName");
|
|
811
|
+
__publicField(this, "clientUri");
|
|
812
|
+
__publicField(this, "logoUri");
|
|
813
|
+
__publicField(this, "policyUri");
|
|
814
|
+
__publicField(this, "clientSecret");
|
|
752
815
|
__publicField(this, "_authUrl");
|
|
753
816
|
__publicField(this, "_clientId");
|
|
754
817
|
__publicField(this, "onRedirectCallback");
|
|
755
818
|
__publicField(this, "tokenExpiresAt");
|
|
756
|
-
this.
|
|
819
|
+
this.identity = options.identity;
|
|
820
|
+
this.serverId = options.serverId;
|
|
821
|
+
this.sessionId = options.sessionId;
|
|
822
|
+
this.redirectUrl = options.redirectUrl;
|
|
823
|
+
this.clientName = options.clientName;
|
|
824
|
+
this.clientUri = options.clientUri;
|
|
825
|
+
this.logoUri = options.logoUri;
|
|
826
|
+
this.policyUri = options.policyUri;
|
|
827
|
+
this._clientId = options.clientId;
|
|
828
|
+
this.clientSecret = options.clientSecret;
|
|
829
|
+
this.onRedirectCallback = options.onRedirect;
|
|
757
830
|
}
|
|
758
831
|
get clientMetadata() {
|
|
759
832
|
return {
|
|
760
|
-
client_name: this.clientName,
|
|
761
|
-
client_uri: this.clientUri,
|
|
833
|
+
client_name: this.clientName || DEFAULT_CLIENT_NAME,
|
|
834
|
+
client_uri: this.clientUri || DEFAULT_CLIENT_URI,
|
|
835
|
+
logo_uri: this.logoUri || DEFAULT_LOGO_URI,
|
|
836
|
+
policy_uri: this.policyUri || DEFAULT_POLICY_URI,
|
|
762
837
|
grant_types: ["authorization_code", "refresh_token"],
|
|
763
838
|
redirect_uris: [this.redirectUrl],
|
|
764
839
|
response_types: ["code"],
|
|
765
|
-
token_endpoint_auth_method: "none",
|
|
766
|
-
|
|
840
|
+
token_endpoint_auth_method: this.clientSecret ? "client_secret_basic" : "none",
|
|
841
|
+
software_id: SOFTWARE_ID,
|
|
842
|
+
software_version: SOFTWARE_VERSION
|
|
767
843
|
};
|
|
768
844
|
}
|
|
769
|
-
get clientUri() {
|
|
770
|
-
return new URL(this.redirectUrl).origin;
|
|
771
|
-
}
|
|
772
|
-
get redirectUrl() {
|
|
773
|
-
return this.baseRedirectUrl;
|
|
774
|
-
}
|
|
775
845
|
get clientId() {
|
|
776
846
|
return this._clientId;
|
|
777
847
|
}
|
|
@@ -806,7 +876,16 @@ var StorageOAuthClientProvider = class {
|
|
|
806
876
|
if (data.clientId && !this._clientId) {
|
|
807
877
|
this._clientId = data.clientId;
|
|
808
878
|
}
|
|
809
|
-
|
|
879
|
+
if (data.clientInformation) {
|
|
880
|
+
return data.clientInformation;
|
|
881
|
+
}
|
|
882
|
+
if (!this._clientId) {
|
|
883
|
+
return void 0;
|
|
884
|
+
}
|
|
885
|
+
return {
|
|
886
|
+
client_id: this._clientId,
|
|
887
|
+
...this.clientSecret ? { client_secret: this.clientSecret } : {}
|
|
888
|
+
};
|
|
810
889
|
}
|
|
811
890
|
/**
|
|
812
891
|
* Stores OAuth client information
|
|
@@ -834,14 +913,14 @@ var StorageOAuthClientProvider = class {
|
|
|
834
913
|
async state() {
|
|
835
914
|
return this.sessionId;
|
|
836
915
|
}
|
|
837
|
-
async checkState(
|
|
916
|
+
async checkState(_state) {
|
|
838
917
|
const data = await storage.getSession(this.identity, this.sessionId);
|
|
839
918
|
if (!data) {
|
|
840
919
|
return { valid: false, error: "Session not found" };
|
|
841
920
|
}
|
|
842
921
|
return { valid: true, serverId: this.serverId };
|
|
843
922
|
}
|
|
844
|
-
async consumeState(
|
|
923
|
+
async consumeState(_state) {
|
|
845
924
|
}
|
|
846
925
|
async redirectToAuthorization(authUrl) {
|
|
847
926
|
this._authUrl = authUrl.toString();
|
|
@@ -853,7 +932,6 @@ var StorageOAuthClientProvider = class {
|
|
|
853
932
|
if (scope === "all") {
|
|
854
933
|
await storage.removeSession(this.identity, this.sessionId);
|
|
855
934
|
} else {
|
|
856
|
-
await this.getSessionData();
|
|
857
935
|
const updates = {};
|
|
858
936
|
if (scope === "client") {
|
|
859
937
|
updates.clientInformation = void 0;
|
|
@@ -1173,41 +1251,33 @@ var MCPClient = class _MCPClient {
|
|
|
1173
1251
|
if (!this.serverUrl || !this.callbackUrl || !this.serverId) {
|
|
1174
1252
|
throw new Error("Missing required connection metadata");
|
|
1175
1253
|
}
|
|
1176
|
-
const clientMetadata = {
|
|
1177
|
-
client_name: this.clientName || "MCP Assistant",
|
|
1178
|
-
redirect_uris: [this.callbackUrl],
|
|
1179
|
-
token_endpoint_auth_method: this.clientSecret ? "client_secret_basic" : "none",
|
|
1180
|
-
client_uri: this.clientUri || "https://mcp-assistant.in",
|
|
1181
|
-
logo_uri: this.logoUri || "https://mcp-assistant.in/logo.png",
|
|
1182
|
-
policy_uri: this.policyUri || "https://mcp-assistant.in/privacy",
|
|
1183
|
-
...this.clientId ? { client_id: this.clientId } : {},
|
|
1184
|
-
...this.clientSecret ? { client_secret: this.clientSecret } : {}
|
|
1185
|
-
};
|
|
1186
1254
|
if (!this.oauthProvider) {
|
|
1187
1255
|
if (!this.serverId) {
|
|
1188
1256
|
throw new Error("serverId required for OAuth provider initialization");
|
|
1189
1257
|
}
|
|
1190
|
-
this.oauthProvider = new StorageOAuthClientProvider(
|
|
1191
|
-
this.identity,
|
|
1192
|
-
this.serverId,
|
|
1193
|
-
this.sessionId,
|
|
1194
|
-
|
|
1195
|
-
this.
|
|
1196
|
-
|
|
1258
|
+
this.oauthProvider = new StorageOAuthClientProvider({
|
|
1259
|
+
identity: this.identity,
|
|
1260
|
+
serverId: this.serverId,
|
|
1261
|
+
sessionId: this.sessionId,
|
|
1262
|
+
redirectUrl: this.callbackUrl,
|
|
1263
|
+
clientName: this.clientName,
|
|
1264
|
+
clientUri: this.clientUri,
|
|
1265
|
+
logoUri: this.logoUri,
|
|
1266
|
+
policyUri: this.policyUri,
|
|
1267
|
+
clientId: this.clientId,
|
|
1268
|
+
clientSecret: this.clientSecret,
|
|
1269
|
+
onRedirect: (redirectUrl) => {
|
|
1197
1270
|
if (this.onRedirect) {
|
|
1198
1271
|
this.onRedirect(redirectUrl);
|
|
1199
1272
|
}
|
|
1200
1273
|
}
|
|
1201
|
-
);
|
|
1202
|
-
if (this.clientId && this.oauthProvider) {
|
|
1203
|
-
this.oauthProvider.clientId = this.clientId;
|
|
1204
|
-
}
|
|
1274
|
+
});
|
|
1205
1275
|
}
|
|
1206
1276
|
if (!this.client) {
|
|
1207
1277
|
this.client = new Client(
|
|
1208
1278
|
{
|
|
1209
|
-
name:
|
|
1210
|
-
version:
|
|
1279
|
+
name: MCP_CLIENT_NAME,
|
|
1280
|
+
version: MCP_CLIENT_VERSION
|
|
1211
1281
|
},
|
|
1212
1282
|
{
|
|
1213
1283
|
capabilities: {
|
|
@@ -1402,8 +1472,8 @@ var MCPClient = class _MCPClient {
|
|
|
1402
1472
|
this.emitProgress("Creating authenticated client...");
|
|
1403
1473
|
this.client = new Client(
|
|
1404
1474
|
{
|
|
1405
|
-
name:
|
|
1406
|
-
version:
|
|
1475
|
+
name: MCP_CLIENT_NAME,
|
|
1476
|
+
version: MCP_CLIENT_VERSION
|
|
1407
1477
|
},
|
|
1408
1478
|
{
|
|
1409
1479
|
capabilities: {
|
|
@@ -1698,8 +1768,8 @@ var MCPClient = class _MCPClient {
|
|
|
1698
1768
|
}
|
|
1699
1769
|
this.client = new Client(
|
|
1700
1770
|
{
|
|
1701
|
-
name:
|
|
1702
|
-
version:
|
|
1771
|
+
name: MCP_CLIENT_NAME,
|
|
1772
|
+
version: MCP_CLIENT_VERSION
|
|
1703
1773
|
},
|
|
1704
1774
|
{ capabilities: {} }
|
|
1705
1775
|
);
|
|
@@ -1968,6 +2038,27 @@ var MultiSessionClient = class {
|
|
|
1968
2038
|
}
|
|
1969
2039
|
};
|
|
1970
2040
|
|
|
2041
|
+
// src/shared/event-routing.ts
|
|
2042
|
+
function isRpcResponseEvent(event) {
|
|
2043
|
+
return "id" in event && ("result" in event || "error" in event);
|
|
2044
|
+
}
|
|
2045
|
+
function isConnectionEvent(event) {
|
|
2046
|
+
if (!("type" in event)) {
|
|
2047
|
+
return false;
|
|
2048
|
+
}
|
|
2049
|
+
switch (event.type) {
|
|
2050
|
+
case "state_changed":
|
|
2051
|
+
case "tools_discovered":
|
|
2052
|
+
case "auth_required":
|
|
2053
|
+
case "error":
|
|
2054
|
+
case "disconnected":
|
|
2055
|
+
case "progress":
|
|
2056
|
+
return true;
|
|
2057
|
+
default:
|
|
2058
|
+
return false;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
|
|
1971
2062
|
// src/server/handlers/sse-handler.ts
|
|
1972
2063
|
var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
|
|
1973
2064
|
var SSEConnectionManager = class {
|
|
@@ -2110,16 +2201,6 @@ var SSEConnectionManager = class {
|
|
|
2110
2201
|
throw new Error(`Connection already exists for server: ${duplicate.serverUrl || duplicate.serverId} (${duplicate.serverName})`);
|
|
2111
2202
|
}
|
|
2112
2203
|
const sessionId = await storage.generateSessionId();
|
|
2113
|
-
this.emitConnectionEvent({
|
|
2114
|
-
type: "state_changed",
|
|
2115
|
-
sessionId,
|
|
2116
|
-
serverId,
|
|
2117
|
-
serverName,
|
|
2118
|
-
serverUrl,
|
|
2119
|
-
state: "CONNECTING",
|
|
2120
|
-
previousState: "DISCONNECTED",
|
|
2121
|
-
timestamp: Date.now()
|
|
2122
|
-
});
|
|
2123
2204
|
try {
|
|
2124
2205
|
const clientMetadata = await this.getResolvedClientMetadata();
|
|
2125
2206
|
const client = new MCPClient({
|
|
@@ -2130,17 +2211,8 @@ var SSEConnectionManager = class {
|
|
|
2130
2211
|
serverUrl,
|
|
2131
2212
|
callbackUrl,
|
|
2132
2213
|
transportType,
|
|
2133
|
-
...clientMetadata
|
|
2214
|
+
...clientMetadata
|
|
2134
2215
|
// Spread client metadata (clientName, clientUri, logoUri, policyUri)
|
|
2135
|
-
onRedirect: (authUrl) => {
|
|
2136
|
-
this.emitConnectionEvent({
|
|
2137
|
-
type: "auth_required",
|
|
2138
|
-
sessionId,
|
|
2139
|
-
serverId,
|
|
2140
|
-
authUrl,
|
|
2141
|
-
timestamp: Date.now()
|
|
2142
|
-
});
|
|
2143
|
-
}
|
|
2144
2216
|
});
|
|
2145
2217
|
this.clients.set(sessionId, client);
|
|
2146
2218
|
client.onConnectionEvent((event) => {
|
|
@@ -2150,20 +2222,19 @@ var SSEConnectionManager = class {
|
|
|
2150
2222
|
this.sendEvent(event);
|
|
2151
2223
|
});
|
|
2152
2224
|
await client.connect();
|
|
2153
|
-
|
|
2154
|
-
this.emitConnectionEvent({
|
|
2155
|
-
type: "tools_discovered",
|
|
2156
|
-
sessionId,
|
|
2157
|
-
serverId,
|
|
2158
|
-
toolCount: tools.tools.length,
|
|
2159
|
-
tools: tools.tools,
|
|
2160
|
-
timestamp: Date.now()
|
|
2161
|
-
});
|
|
2225
|
+
await client.listTools();
|
|
2162
2226
|
return {
|
|
2163
2227
|
sessionId,
|
|
2164
2228
|
success: true
|
|
2165
2229
|
};
|
|
2166
2230
|
} catch (error) {
|
|
2231
|
+
if (error instanceof UnauthorizedError) {
|
|
2232
|
+
this.clients.delete(sessionId);
|
|
2233
|
+
return {
|
|
2234
|
+
sessionId,
|
|
2235
|
+
success: true
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2167
2238
|
this.emitConnectionEvent({
|
|
2168
2239
|
type: "error",
|
|
2169
2240
|
sessionId,
|
|
@@ -2265,14 +2336,6 @@ var SSEConnectionManager = class {
|
|
|
2265
2336
|
await client.connect();
|
|
2266
2337
|
this.clients.set(sessionId, client);
|
|
2267
2338
|
const tools = await client.listTools();
|
|
2268
|
-
this.emitConnectionEvent({
|
|
2269
|
-
type: "tools_discovered",
|
|
2270
|
-
sessionId,
|
|
2271
|
-
serverId: session.serverId ?? "unknown",
|
|
2272
|
-
toolCount: tools.tools.length,
|
|
2273
|
-
tools: tools.tools,
|
|
2274
|
-
timestamp: Date.now()
|
|
2275
|
-
});
|
|
2276
2339
|
return { success: true, toolCount: tools.tools.length };
|
|
2277
2340
|
} catch (error) {
|
|
2278
2341
|
this.emitConnectionEvent({
|
|
@@ -2295,16 +2358,6 @@ var SSEConnectionManager = class {
|
|
|
2295
2358
|
if (!session) {
|
|
2296
2359
|
throw new Error("Session not found");
|
|
2297
2360
|
}
|
|
2298
|
-
this.emitConnectionEvent({
|
|
2299
|
-
type: "state_changed",
|
|
2300
|
-
sessionId,
|
|
2301
|
-
serverId: session.serverId ?? "unknown",
|
|
2302
|
-
serverName: session.serverName ?? "Unknown",
|
|
2303
|
-
serverUrl: session.serverUrl,
|
|
2304
|
-
state: "AUTHENTICATING",
|
|
2305
|
-
previousState: "DISCONNECTED",
|
|
2306
|
-
timestamp: Date.now()
|
|
2307
|
-
});
|
|
2308
2361
|
try {
|
|
2309
2362
|
const client = new MCPClient({
|
|
2310
2363
|
identity: this.identity,
|
|
@@ -2314,14 +2367,6 @@ var SSEConnectionManager = class {
|
|
|
2314
2367
|
await client.finishAuth(code);
|
|
2315
2368
|
this.clients.set(sessionId, client);
|
|
2316
2369
|
const tools = await client.listTools();
|
|
2317
|
-
this.emitConnectionEvent({
|
|
2318
|
-
type: "tools_discovered",
|
|
2319
|
-
sessionId,
|
|
2320
|
-
serverId: session.serverId ?? "unknown",
|
|
2321
|
-
toolCount: tools.tools.length,
|
|
2322
|
-
tools: tools.tools,
|
|
2323
|
-
timestamp: Date.now()
|
|
2324
|
-
});
|
|
2325
2370
|
return { success: true, toolCount: tools.tools.length };
|
|
2326
2371
|
} catch (error) {
|
|
2327
2372
|
this.emitConnectionEvent({
|
|
@@ -2399,9 +2444,9 @@ function createSSEHandler(options) {
|
|
|
2399
2444
|
});
|
|
2400
2445
|
writeSSEEvent(res, "connected", { timestamp: Date.now() });
|
|
2401
2446
|
const manager = new SSEConnectionManager(options, (event) => {
|
|
2402
|
-
if (
|
|
2447
|
+
if (isRpcResponseEvent(event)) {
|
|
2403
2448
|
writeSSEEvent(res, "rpc-response", event);
|
|
2404
|
-
} else if (
|
|
2449
|
+
} else if (isConnectionEvent(event)) {
|
|
2405
2450
|
writeSSEEvent(res, "connection", event);
|
|
2406
2451
|
} else {
|
|
2407
2452
|
writeSSEEvent(res, "observability", event);
|
|
@@ -2432,9 +2477,6 @@ function writeSSEEvent(res, event, data) {
|
|
|
2432
2477
|
}
|
|
2433
2478
|
|
|
2434
2479
|
// src/server/handlers/nextjs-handler.ts
|
|
2435
|
-
function isRpcResponseEvent(event) {
|
|
2436
|
-
return "id" in event && ("result" in event || "error" in event);
|
|
2437
|
-
}
|
|
2438
2480
|
function createNextMcpHandler(options = {}) {
|
|
2439
2481
|
const {
|
|
2440
2482
|
getIdentity = (request) => new URL(request.url).searchParams.get("identity"),
|
|
@@ -2525,7 +2567,7 @@ data: ${JSON.stringify(data)}
|
|
|
2525
2567
|
(event) => {
|
|
2526
2568
|
if (isRpcResponseEvent(event)) {
|
|
2527
2569
|
sendSSE("rpc-response", event);
|
|
2528
|
-
} else if (
|
|
2570
|
+
} else if (isConnectionEvent(event)) {
|
|
2529
2571
|
sendSSE("connection", event);
|
|
2530
2572
|
} else {
|
|
2531
2573
|
sendSSE("observability", event);
|