@mcp-ts/sdk 1.3.4 → 1.3.6
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 -400
- 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/bin/mcp-ts.d.mts +1 -0
- package/dist/bin/mcp-ts.d.ts +1 -0
- package/dist/bin/mcp-ts.js +105 -0
- package/dist/bin/mcp-ts.js.map +1 -0
- package/dist/bin/mcp-ts.mjs +82 -0
- package/dist/bin/mcp-ts.mjs.map +1 -0
- 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 +480 -179
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +418 -179
- 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 +455 -172
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +410 -172
- 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 +19 -6
- package/src/bin/mcp-ts.ts +102 -0
- 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 +204 -207
- package/src/server/handlers/sse-handler.ts +14 -63
- package/src/server/mcp/oauth-client.ts +67 -79
- package/src/server/mcp/storage-oauth-provider.ts +71 -38
- package/src/server/storage/file-backend.ts +1 -0
- package/src/server/storage/index.ts +82 -38
- package/src/server/storage/memory-backend.ts +4 -0
- package/src/server/storage/redis-backend.ts +102 -23
- package/src/server/storage/sqlite-backend.ts +1 -0
- package/src/server/storage/supabase-backend.ts +227 -0
- package/src/server/storage/types.ts +12 -12
- package/src/shared/constants.ts +2 -2
- package/src/shared/event-routing.ts +28 -0
- package/supabase/migrations/20260330195700_install_mcp_sessions.sql +84 -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,16 @@ 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");
|
|
143
|
+
}
|
|
144
|
+
async init() {
|
|
145
|
+
try {
|
|
146
|
+
await this.redis.ping();
|
|
147
|
+
console.log("[mcp-ts][Storage] Redis: \u2713 Connected to server.");
|
|
148
|
+
} catch (error) {
|
|
149
|
+
throw new Error(`[RedisStorage] Failed to connect to Redis: ${error.message}`);
|
|
150
|
+
}
|
|
133
151
|
}
|
|
134
152
|
/**
|
|
135
153
|
* Generates Redis key for a specific session
|
|
@@ -143,7 +161,34 @@ var RedisStorageBackend = class {
|
|
|
143
161
|
* @private
|
|
144
162
|
*/
|
|
145
163
|
getIdentityKey(identity) {
|
|
146
|
-
return
|
|
164
|
+
return `${this.IDENTITY_KEY_PREFIX}${identity}${this.IDENTITY_KEY_SUFFIX}`;
|
|
165
|
+
}
|
|
166
|
+
parseIdentityFromKey(identityKey) {
|
|
167
|
+
return identityKey.slice(
|
|
168
|
+
this.IDENTITY_KEY_PREFIX.length,
|
|
169
|
+
identityKey.length - this.IDENTITY_KEY_SUFFIX.length
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
async scanKeys(pattern) {
|
|
173
|
+
const redis2 = this.redis;
|
|
174
|
+
if (typeof redis2.scan !== "function") {
|
|
175
|
+
return await this.redis.keys(pattern);
|
|
176
|
+
}
|
|
177
|
+
const keys = /* @__PURE__ */ new Set();
|
|
178
|
+
let cursor = "0";
|
|
179
|
+
try {
|
|
180
|
+
do {
|
|
181
|
+
const [nextCursor, batch] = await redis2.scan(cursor, "MATCH", pattern, "COUNT", 100);
|
|
182
|
+
cursor = nextCursor;
|
|
183
|
+
for (const key of batch) {
|
|
184
|
+
keys.add(key);
|
|
185
|
+
}
|
|
186
|
+
} while (cursor !== "0");
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.warn("[RedisStorage] SCAN failed, falling back to KEYS:", error);
|
|
189
|
+
return await this.redis.keys(pattern);
|
|
190
|
+
}
|
|
191
|
+
return Array.from(keys);
|
|
147
192
|
}
|
|
148
193
|
generateSessionId() {
|
|
149
194
|
return firstChar() + rest();
|
|
@@ -211,17 +256,13 @@ var RedisStorageBackend = class {
|
|
|
211
256
|
}
|
|
212
257
|
}
|
|
213
258
|
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
|
-
}
|
|
259
|
+
const sessions = await this.getIdentitySessionsData(identity);
|
|
260
|
+
return sessions.map((session) => session.sessionId);
|
|
221
261
|
}
|
|
222
262
|
async getIdentitySessionsData(identity) {
|
|
223
263
|
try {
|
|
224
|
-
const
|
|
264
|
+
const identityKey = this.getIdentityKey(identity);
|
|
265
|
+
const sessionIds = await this.redis.smembers(identityKey);
|
|
225
266
|
if (sessionIds.length === 0) return [];
|
|
226
267
|
const results = await Promise.all(
|
|
227
268
|
sessionIds.map(async (sessionId) => {
|
|
@@ -229,6 +270,10 @@ var RedisStorageBackend = class {
|
|
|
229
270
|
return data ? JSON.parse(data) : null;
|
|
230
271
|
})
|
|
231
272
|
);
|
|
273
|
+
const staleSessionIds = sessionIds.filter((_, index) => results[index] === null);
|
|
274
|
+
if (staleSessionIds.length > 0) {
|
|
275
|
+
await this.redis.srem(identityKey, ...staleSessionIds);
|
|
276
|
+
}
|
|
232
277
|
return results.filter((session) => session !== null);
|
|
233
278
|
} catch (error) {
|
|
234
279
|
console.error(`[RedisStorage] Failed to get session data for ${identity}:`, error);
|
|
@@ -247,9 +292,22 @@ var RedisStorageBackend = class {
|
|
|
247
292
|
}
|
|
248
293
|
async getAllSessionIds() {
|
|
249
294
|
try {
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
|
|
295
|
+
const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
|
|
296
|
+
const sessions = await Promise.all(
|
|
297
|
+
keys.map(async (key) => {
|
|
298
|
+
const data = await this.redis.get(key);
|
|
299
|
+
if (!data) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
return JSON.parse(data).sessionId;
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.error("[RedisStorage] Failed to parse session while listing all session IDs:", error);
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
return sessions.filter((sessionId) => sessionId !== null);
|
|
253
311
|
} catch (error) {
|
|
254
312
|
console.error("[RedisStorage] Failed to get all sessions:", error);
|
|
255
313
|
return [];
|
|
@@ -257,10 +315,11 @@ var RedisStorageBackend = class {
|
|
|
257
315
|
}
|
|
258
316
|
async clearAll() {
|
|
259
317
|
try {
|
|
260
|
-
const
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
318
|
+
const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
|
|
319
|
+
const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
|
|
320
|
+
const allKeys = [...keys, ...identityKeys];
|
|
321
|
+
if (allKeys.length > 0) {
|
|
322
|
+
await this.redis.del(...allKeys);
|
|
264
323
|
}
|
|
265
324
|
} catch (error) {
|
|
266
325
|
console.error("[RedisStorage] Failed to clear sessions:", error);
|
|
@@ -268,12 +327,24 @@ var RedisStorageBackend = class {
|
|
|
268
327
|
}
|
|
269
328
|
async cleanupExpiredSessions() {
|
|
270
329
|
try {
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
276
|
-
await this.redis.del(
|
|
330
|
+
const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
|
|
331
|
+
for (const identityKey of identityKeys) {
|
|
332
|
+
const identity = this.parseIdentityFromKey(identityKey);
|
|
333
|
+
const sessionIds = await this.redis.smembers(identityKey);
|
|
334
|
+
if (sessionIds.length === 0) {
|
|
335
|
+
await this.redis.del(identityKey);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
const existenceChecks = await Promise.all(
|
|
339
|
+
sessionIds.map((sessionId) => this.redis.exists(this.getSessionKey(identity, sessionId)))
|
|
340
|
+
);
|
|
341
|
+
const staleSessionIds = sessionIds.filter((_, index) => existenceChecks[index] === 0);
|
|
342
|
+
if (staleSessionIds.length > 0) {
|
|
343
|
+
await this.redis.srem(identityKey, ...staleSessionIds);
|
|
344
|
+
}
|
|
345
|
+
const remainingCount = await this.redis.scard(identityKey);
|
|
346
|
+
if (remainingCount === 0) {
|
|
347
|
+
await this.redis.del(identityKey);
|
|
277
348
|
}
|
|
278
349
|
}
|
|
279
350
|
} catch (error) {
|
|
@@ -303,6 +374,9 @@ var MemoryStorageBackend = class {
|
|
|
303
374
|
// Map<identity, Set<sessionId>>
|
|
304
375
|
__publicField(this, "identitySessions", /* @__PURE__ */ new Map());
|
|
305
376
|
}
|
|
377
|
+
async init() {
|
|
378
|
+
console.log("[mcp-ts][Storage] Memory: \u2713 internal memory store active.");
|
|
379
|
+
}
|
|
306
380
|
getSessionKey(identity, sessionId) {
|
|
307
381
|
return `${identity}:${sessionId}`;
|
|
308
382
|
}
|
|
@@ -422,6 +496,7 @@ var FileStorageBackend = class {
|
|
|
422
496
|
}
|
|
423
497
|
}
|
|
424
498
|
this.initialized = true;
|
|
499
|
+
console.log(`[mcp-ts][Storage] File: \u2713 storage directory at ${path.dirname(this.filePath)} verified.`);
|
|
425
500
|
}
|
|
426
501
|
async ensureInitialized() {
|
|
427
502
|
if (!this.initialized) await this.init();
|
|
@@ -526,6 +601,7 @@ var SqliteStorage = class {
|
|
|
526
601
|
CREATE INDEX IF NOT EXISTS idx_${this.table}_identity ON ${this.table}(identity);
|
|
527
602
|
`);
|
|
528
603
|
this.initialized = true;
|
|
604
|
+
console.log(`[mcp-ts][Storage] SQLite: \u2713 database at ${this.dbPath} verified.`);
|
|
529
605
|
} catch (error) {
|
|
530
606
|
if (error.code === "MODULE_NOT_FOUND" || error.message?.includes("better-sqlite3")) {
|
|
531
607
|
throw new Error(
|
|
@@ -641,9 +717,166 @@ var SqliteStorage = class {
|
|
|
641
717
|
}
|
|
642
718
|
};
|
|
643
719
|
|
|
720
|
+
// src/server/storage/supabase-backend.ts
|
|
721
|
+
var SupabaseStorageBackend = class {
|
|
722
|
+
constructor(supabase) {
|
|
723
|
+
this.supabase = supabase;
|
|
724
|
+
__publicField(this, "DEFAULT_TTL", SESSION_TTL_SECONDS);
|
|
725
|
+
}
|
|
726
|
+
async init() {
|
|
727
|
+
const { error } = await this.supabase.from("mcp_sessions").select("session_id").limit(0);
|
|
728
|
+
if (error) {
|
|
729
|
+
if (error.code === "42P01") {
|
|
730
|
+
throw new Error(
|
|
731
|
+
'[SupabaseStorage] Table "mcp_sessions" not found in your database. Please run "npx mcp-ts supabase-init" in your project to set up the required table and RLS policies.'
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
throw new Error(`[SupabaseStorage] Initialization check failed: ${error.message}`);
|
|
735
|
+
}
|
|
736
|
+
console.log('[mcp-ts][Storage] Supabase: \u2713 "mcp_sessions" table verified.');
|
|
737
|
+
}
|
|
738
|
+
generateSessionId() {
|
|
739
|
+
return crypto.randomUUID();
|
|
740
|
+
}
|
|
741
|
+
mapRowToSessionData(row) {
|
|
742
|
+
return {
|
|
743
|
+
sessionId: row.session_id,
|
|
744
|
+
serverId: row.server_id,
|
|
745
|
+
serverName: row.server_name,
|
|
746
|
+
serverUrl: row.server_url,
|
|
747
|
+
transportType: row.transport_type,
|
|
748
|
+
callbackUrl: row.callback_url,
|
|
749
|
+
createdAt: new Date(row.created_at).getTime(),
|
|
750
|
+
identity: row.identity,
|
|
751
|
+
headers: row.headers,
|
|
752
|
+
active: row.active,
|
|
753
|
+
clientInformation: row.client_information,
|
|
754
|
+
tokens: row.tokens,
|
|
755
|
+
codeVerifier: row.code_verifier,
|
|
756
|
+
clientId: row.client_id
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
async createSession(session, ttl) {
|
|
760
|
+
const { sessionId, identity } = session;
|
|
761
|
+
if (!sessionId || !identity) throw new Error("identity and sessionId required");
|
|
762
|
+
const effectiveTtl = ttl ?? this.DEFAULT_TTL;
|
|
763
|
+
const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
|
|
764
|
+
const { error } = await this.supabase.from("mcp_sessions").insert({
|
|
765
|
+
session_id: sessionId,
|
|
766
|
+
user_id: identity,
|
|
767
|
+
// Maps user_id to identity to support RLS using auth.uid()
|
|
768
|
+
server_id: session.serverId,
|
|
769
|
+
server_name: session.serverName,
|
|
770
|
+
server_url: session.serverUrl,
|
|
771
|
+
transport_type: session.transportType,
|
|
772
|
+
callback_url: session.callbackUrl,
|
|
773
|
+
created_at: new Date(session.createdAt || Date.now()).toISOString(),
|
|
774
|
+
identity,
|
|
775
|
+
headers: session.headers,
|
|
776
|
+
active: session.active ?? false,
|
|
777
|
+
client_information: session.clientInformation,
|
|
778
|
+
tokens: session.tokens,
|
|
779
|
+
code_verifier: session.codeVerifier,
|
|
780
|
+
client_id: session.clientId,
|
|
781
|
+
expires_at: expiresAt
|
|
782
|
+
});
|
|
783
|
+
if (error) {
|
|
784
|
+
if (error.code === "23505") {
|
|
785
|
+
throw new Error(`Session ${sessionId} already exists`);
|
|
786
|
+
}
|
|
787
|
+
throw new Error(`Failed to create session in Supabase: ${error.message}`);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
async updateSession(identity, sessionId, data, ttl) {
|
|
791
|
+
const effectiveTtl = ttl ?? this.DEFAULT_TTL;
|
|
792
|
+
const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
|
|
793
|
+
const updateData = {
|
|
794
|
+
expires_at: expiresAt,
|
|
795
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
796
|
+
};
|
|
797
|
+
if ("serverId" in data) updateData.server_id = data.serverId;
|
|
798
|
+
if ("serverName" in data) updateData.server_name = data.serverName;
|
|
799
|
+
if ("serverUrl" in data) updateData.server_url = data.serverUrl;
|
|
800
|
+
if ("transportType" in data) updateData.transport_type = data.transportType;
|
|
801
|
+
if ("callbackUrl" in data) updateData.callback_url = data.callbackUrl;
|
|
802
|
+
if ("active" in data) updateData.active = data.active;
|
|
803
|
+
if ("headers" in data) updateData.headers = data.headers;
|
|
804
|
+
if ("clientInformation" in data) updateData.client_information = data.clientInformation;
|
|
805
|
+
if ("tokens" in data) updateData.tokens = data.tokens;
|
|
806
|
+
if ("codeVerifier" in data) updateData.code_verifier = data.codeVerifier;
|
|
807
|
+
if ("clientId" in data) updateData.client_id = data.clientId;
|
|
808
|
+
const { data: updatedRows, error } = await this.supabase.from("mcp_sessions").update(updateData).eq("identity", identity).eq("session_id", sessionId).select("id");
|
|
809
|
+
if (error) {
|
|
810
|
+
throw new Error(`Failed to update session: ${error.message}`);
|
|
811
|
+
}
|
|
812
|
+
if (!updatedRows || updatedRows.length === 0) {
|
|
813
|
+
throw new Error(`Session ${sessionId} not found for identity ${identity}`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
async getSession(identity, sessionId) {
|
|
817
|
+
const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("identity", identity).eq("session_id", sessionId).maybeSingle();
|
|
818
|
+
if (error) {
|
|
819
|
+
console.error("[SupabaseStorage] Failed to get session:", error);
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
if (!data) return null;
|
|
823
|
+
return this.mapRowToSessionData(data);
|
|
824
|
+
}
|
|
825
|
+
async getIdentitySessionsData(identity) {
|
|
826
|
+
const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("identity", identity);
|
|
827
|
+
if (error) {
|
|
828
|
+
console.error(`[SupabaseStorage] Failed to get session data for ${identity}:`, error);
|
|
829
|
+
return [];
|
|
830
|
+
}
|
|
831
|
+
return data.map((row) => this.mapRowToSessionData(row));
|
|
832
|
+
}
|
|
833
|
+
async removeSession(identity, sessionId) {
|
|
834
|
+
const { error } = await this.supabase.from("mcp_sessions").delete().eq("identity", identity).eq("session_id", sessionId);
|
|
835
|
+
if (error) {
|
|
836
|
+
console.error("[SupabaseStorage] Failed to remove session:", error);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
async getIdentityMcpSessions(identity) {
|
|
840
|
+
const { data, error } = await this.supabase.from("mcp_sessions").select("session_id").eq("identity", identity);
|
|
841
|
+
if (error) {
|
|
842
|
+
console.error(`[SupabaseStorage] Failed to get sessions for ${identity}:`, error);
|
|
843
|
+
return [];
|
|
844
|
+
}
|
|
845
|
+
return data.map((row) => row.session_id);
|
|
846
|
+
}
|
|
847
|
+
async getAllSessionIds() {
|
|
848
|
+
const { data, error } = await this.supabase.from("mcp_sessions").select("session_id");
|
|
849
|
+
if (error) {
|
|
850
|
+
console.error("[SupabaseStorage] Failed to get all sessions:", error);
|
|
851
|
+
return [];
|
|
852
|
+
}
|
|
853
|
+
return data.map((row) => row.session_id);
|
|
854
|
+
}
|
|
855
|
+
async clearAll() {
|
|
856
|
+
const { error } = await this.supabase.from("mcp_sessions").delete().neq("session_id", "");
|
|
857
|
+
if (error) {
|
|
858
|
+
console.error("[SupabaseStorage] Failed to clear sessions:", error);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
async cleanupExpiredSessions() {
|
|
862
|
+
const { error } = await this.supabase.from("mcp_sessions").delete().lt("expires_at", (/* @__PURE__ */ new Date()).toISOString());
|
|
863
|
+
if (error) {
|
|
864
|
+
console.error("[SupabaseStorage] Failed to cleanup expired sessions:", error);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
async disconnect() {
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
|
|
644
871
|
// src/server/storage/index.ts
|
|
645
872
|
var storageInstance = null;
|
|
646
873
|
var storagePromise = null;
|
|
874
|
+
async function initializeStorage(store) {
|
|
875
|
+
if (typeof store.init === "function") {
|
|
876
|
+
await store.init();
|
|
877
|
+
}
|
|
878
|
+
return store;
|
|
879
|
+
}
|
|
647
880
|
async function createStorage() {
|
|
648
881
|
const type = process.env.MCP_TS_STORAGE_TYPE?.toLowerCase();
|
|
649
882
|
if (type === "redis") {
|
|
@@ -653,68 +886,95 @@ async function createStorage() {
|
|
|
653
886
|
try {
|
|
654
887
|
const { getRedis: getRedis2 } = await Promise.resolve().then(() => (init_redis(), redis_exports));
|
|
655
888
|
const redis2 = await getRedis2();
|
|
656
|
-
console.log(
|
|
657
|
-
return new RedisStorageBackend(redis2);
|
|
889
|
+
console.log('[mcp-ts][Storage] Explicit selection: "redis"');
|
|
890
|
+
return await initializeStorage(new RedisStorageBackend(redis2));
|
|
658
891
|
} catch (error) {
|
|
659
|
-
console.error("[Storage] Failed to initialize Redis:", error.message);
|
|
660
|
-
console.log("[Storage] Falling back to In-Memory storage");
|
|
661
|
-
return new MemoryStorageBackend();
|
|
892
|
+
console.error("[mcp-ts][Storage] Failed to initialize Redis:", error.message);
|
|
893
|
+
console.log("[mcp-ts][Storage] Falling back to In-Memory storage");
|
|
894
|
+
return await initializeStorage(new MemoryStorageBackend());
|
|
662
895
|
}
|
|
663
896
|
}
|
|
664
897
|
if (type === "file") {
|
|
665
898
|
const filePath = process.env.MCP_TS_STORAGE_FILE;
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
}
|
|
669
|
-
console.log(`[Storage] Using File storage (${filePath}) (Explicit)`);
|
|
670
|
-
const store = new FileStorageBackend({ path: filePath });
|
|
671
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
672
|
-
return store;
|
|
899
|
+
console.log(`[mcp-ts][Storage] Explicit selection: "file" (${filePath || "default"})`);
|
|
900
|
+
return await initializeStorage(new FileStorageBackend({ path: filePath }));
|
|
673
901
|
}
|
|
674
902
|
if (type === "sqlite") {
|
|
675
903
|
const dbPath = process.env.MCP_TS_STORAGE_SQLITE_PATH;
|
|
676
|
-
console.log(`[Storage]
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
904
|
+
console.log(`[mcp-ts][Storage] Explicit selection: "sqlite" (${dbPath || "default"})`);
|
|
905
|
+
return await initializeStorage(new SqliteStorage({ path: dbPath }));
|
|
906
|
+
}
|
|
907
|
+
if (type === "supabase") {
|
|
908
|
+
const url = process.env.SUPABASE_URL;
|
|
909
|
+
const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
|
|
910
|
+
if (!url || !key) {
|
|
911
|
+
console.warn('[mcp-ts][Storage] Explicit selection "supabase" requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY.');
|
|
912
|
+
} else {
|
|
913
|
+
if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
|
|
914
|
+
console.warn('[mcp-ts][Storage] \u26A0\uFE0F Warning: Using "SUPABASE_ANON_KEY" for server-side storage. You may encounter RLS policy violations. "SUPABASE_SERVICE_ROLE_KEY" is recommended.');
|
|
915
|
+
}
|
|
916
|
+
try {
|
|
917
|
+
const { createClient } = await import('@supabase/supabase-js');
|
|
918
|
+
const client = createClient(url, key);
|
|
919
|
+
console.log('[mcp-ts][Storage] Explicit selection: "supabase"');
|
|
920
|
+
return await initializeStorage(new SupabaseStorageBackend(client));
|
|
921
|
+
} catch (error) {
|
|
922
|
+
console.error("[mcp-ts][Storage] Failed to initialize Supabase:", error.message);
|
|
923
|
+
console.log("[mcp-ts][Storage] Falling back to In-Memory storage");
|
|
924
|
+
return await initializeStorage(new MemoryStorageBackend());
|
|
925
|
+
}
|
|
926
|
+
}
|
|
680
927
|
}
|
|
681
928
|
if (type === "memory") {
|
|
682
|
-
console.log(
|
|
683
|
-
return new MemoryStorageBackend();
|
|
929
|
+
console.log('[mcp-ts][Storage] Explicit selection: "memory"');
|
|
930
|
+
return await initializeStorage(new MemoryStorageBackend());
|
|
684
931
|
}
|
|
685
932
|
if (process.env.REDIS_URL) {
|
|
686
933
|
try {
|
|
687
934
|
const { getRedis: getRedis2 } = await Promise.resolve().then(() => (init_redis(), redis_exports));
|
|
688
935
|
const redis2 = await getRedis2();
|
|
689
|
-
console.log(
|
|
690
|
-
return new RedisStorageBackend(redis2);
|
|
936
|
+
console.log('[mcp-ts][Storage] Auto-detection: "redis" (via REDIS_URL)');
|
|
937
|
+
return await initializeStorage(new RedisStorageBackend(redis2));
|
|
691
938
|
} catch (error) {
|
|
692
|
-
console.error("[Storage] Redis auto-detection failed:", error.message);
|
|
693
|
-
console.log("[Storage] Falling back to
|
|
694
|
-
return new MemoryStorageBackend();
|
|
939
|
+
console.error("[mcp-ts][Storage] Redis auto-detection failed:", error.message);
|
|
940
|
+
console.log("[mcp-ts][Storage] Falling back to next available backend");
|
|
695
941
|
}
|
|
696
942
|
}
|
|
697
943
|
if (process.env.MCP_TS_STORAGE_FILE) {
|
|
698
|
-
console.log(`[Storage] Auto-
|
|
699
|
-
|
|
700
|
-
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
701
|
-
return store;
|
|
944
|
+
console.log(`[mcp-ts][Storage] Auto-detection: "file" (${process.env.MCP_TS_STORAGE_FILE})`);
|
|
945
|
+
return await initializeStorage(new FileStorageBackend({ path: process.env.MCP_TS_STORAGE_FILE }));
|
|
702
946
|
}
|
|
703
947
|
if (process.env.MCP_TS_STORAGE_SQLITE_PATH) {
|
|
704
|
-
console.log(`[Storage] Auto-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
948
|
+
console.log(`[mcp-ts][Storage] Auto-detection: "sqlite" (${process.env.MCP_TS_STORAGE_SQLITE_PATH})`);
|
|
949
|
+
return await initializeStorage(new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH }));
|
|
950
|
+
}
|
|
951
|
+
if (process.env.SUPABASE_URL && (process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY)) {
|
|
952
|
+
try {
|
|
953
|
+
const { createClient } = await import('@supabase/supabase-js');
|
|
954
|
+
const url = process.env.SUPABASE_URL;
|
|
955
|
+
const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
|
|
956
|
+
if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
|
|
957
|
+
console.warn('[mcp-ts][Storage] \u26A0\uFE0F Warning: Using "SUPABASE_ANON_KEY" for server-side storage. You may encounter RLS policy violations. "SUPABASE_SERVICE_ROLE_KEY" is recommended.');
|
|
958
|
+
}
|
|
959
|
+
const client = createClient(url, key);
|
|
960
|
+
console.log('[mcp-ts][Storage] Auto-detection: "supabase" (via SUPABASE_URL)');
|
|
961
|
+
return await initializeStorage(new SupabaseStorageBackend(client));
|
|
962
|
+
} catch (error) {
|
|
963
|
+
console.error("[mcp-ts][Storage] Supabase auto-detection failed:", error.message);
|
|
964
|
+
}
|
|
708
965
|
}
|
|
709
|
-
console.log(
|
|
710
|
-
return new MemoryStorageBackend();
|
|
966
|
+
console.log('[mcp-ts][Storage] Defaulting to: "memory"');
|
|
967
|
+
return await initializeStorage(new MemoryStorageBackend());
|
|
711
968
|
}
|
|
712
969
|
async function getStorage() {
|
|
713
970
|
if (storageInstance) {
|
|
714
971
|
return storageInstance;
|
|
715
972
|
}
|
|
716
973
|
if (!storagePromise) {
|
|
717
|
-
storagePromise = createStorage()
|
|
974
|
+
storagePromise = createStorage().catch((error) => {
|
|
975
|
+
storagePromise = null;
|
|
976
|
+
throw error;
|
|
977
|
+
});
|
|
718
978
|
}
|
|
719
979
|
storageInstance = await storagePromise;
|
|
720
980
|
return storageInstance;
|
|
@@ -735,43 +995,49 @@ var storage = new Proxy({}, {
|
|
|
735
995
|
// src/server/mcp/storage-oauth-provider.ts
|
|
736
996
|
var StorageOAuthClientProvider = class {
|
|
737
997
|
/**
|
|
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
|
|
998
|
+
* Creates a new storage-backed OAuth provider
|
|
999
|
+
* @param options - Provider configuration
|
|
745
1000
|
*/
|
|
746
|
-
constructor(
|
|
747
|
-
this
|
|
748
|
-
this
|
|
749
|
-
this
|
|
750
|
-
this
|
|
751
|
-
this
|
|
1001
|
+
constructor(options) {
|
|
1002
|
+
__publicField(this, "identity");
|
|
1003
|
+
__publicField(this, "serverId");
|
|
1004
|
+
__publicField(this, "sessionId");
|
|
1005
|
+
__publicField(this, "redirectUrl");
|
|
1006
|
+
__publicField(this, "clientName");
|
|
1007
|
+
__publicField(this, "clientUri");
|
|
1008
|
+
__publicField(this, "logoUri");
|
|
1009
|
+
__publicField(this, "policyUri");
|
|
1010
|
+
__publicField(this, "clientSecret");
|
|
752
1011
|
__publicField(this, "_authUrl");
|
|
753
1012
|
__publicField(this, "_clientId");
|
|
754
1013
|
__publicField(this, "onRedirectCallback");
|
|
755
1014
|
__publicField(this, "tokenExpiresAt");
|
|
756
|
-
this.
|
|
1015
|
+
this.identity = options.identity;
|
|
1016
|
+
this.serverId = options.serverId;
|
|
1017
|
+
this.sessionId = options.sessionId;
|
|
1018
|
+
this.redirectUrl = options.redirectUrl;
|
|
1019
|
+
this.clientName = options.clientName;
|
|
1020
|
+
this.clientUri = options.clientUri;
|
|
1021
|
+
this.logoUri = options.logoUri;
|
|
1022
|
+
this.policyUri = options.policyUri;
|
|
1023
|
+
this._clientId = options.clientId;
|
|
1024
|
+
this.clientSecret = options.clientSecret;
|
|
1025
|
+
this.onRedirectCallback = options.onRedirect;
|
|
757
1026
|
}
|
|
758
1027
|
get clientMetadata() {
|
|
759
1028
|
return {
|
|
760
|
-
client_name: this.clientName,
|
|
761
|
-
client_uri: this.clientUri,
|
|
1029
|
+
client_name: this.clientName || DEFAULT_CLIENT_NAME,
|
|
1030
|
+
client_uri: this.clientUri || DEFAULT_CLIENT_URI,
|
|
1031
|
+
logo_uri: this.logoUri || DEFAULT_LOGO_URI,
|
|
1032
|
+
policy_uri: this.policyUri || DEFAULT_POLICY_URI,
|
|
762
1033
|
grant_types: ["authorization_code", "refresh_token"],
|
|
763
1034
|
redirect_uris: [this.redirectUrl],
|
|
764
1035
|
response_types: ["code"],
|
|
765
|
-
token_endpoint_auth_method: "none",
|
|
766
|
-
|
|
1036
|
+
token_endpoint_auth_method: this.clientSecret ? "client_secret_basic" : "none",
|
|
1037
|
+
software_id: SOFTWARE_ID,
|
|
1038
|
+
software_version: SOFTWARE_VERSION
|
|
767
1039
|
};
|
|
768
1040
|
}
|
|
769
|
-
get clientUri() {
|
|
770
|
-
return new URL(this.redirectUrl).origin;
|
|
771
|
-
}
|
|
772
|
-
get redirectUrl() {
|
|
773
|
-
return this.baseRedirectUrl;
|
|
774
|
-
}
|
|
775
1041
|
get clientId() {
|
|
776
1042
|
return this._clientId;
|
|
777
1043
|
}
|
|
@@ -806,7 +1072,16 @@ var StorageOAuthClientProvider = class {
|
|
|
806
1072
|
if (data.clientId && !this._clientId) {
|
|
807
1073
|
this._clientId = data.clientId;
|
|
808
1074
|
}
|
|
809
|
-
|
|
1075
|
+
if (data.clientInformation) {
|
|
1076
|
+
return data.clientInformation;
|
|
1077
|
+
}
|
|
1078
|
+
if (!this._clientId) {
|
|
1079
|
+
return void 0;
|
|
1080
|
+
}
|
|
1081
|
+
return {
|
|
1082
|
+
client_id: this._clientId,
|
|
1083
|
+
...this.clientSecret ? { client_secret: this.clientSecret } : {}
|
|
1084
|
+
};
|
|
810
1085
|
}
|
|
811
1086
|
/**
|
|
812
1087
|
* Stores OAuth client information
|
|
@@ -834,14 +1109,14 @@ var StorageOAuthClientProvider = class {
|
|
|
834
1109
|
async state() {
|
|
835
1110
|
return this.sessionId;
|
|
836
1111
|
}
|
|
837
|
-
async checkState(
|
|
1112
|
+
async checkState(_state) {
|
|
838
1113
|
const data = await storage.getSession(this.identity, this.sessionId);
|
|
839
1114
|
if (!data) {
|
|
840
1115
|
return { valid: false, error: "Session not found" };
|
|
841
1116
|
}
|
|
842
1117
|
return { valid: true, serverId: this.serverId };
|
|
843
1118
|
}
|
|
844
|
-
async consumeState(
|
|
1119
|
+
async consumeState(_state) {
|
|
845
1120
|
}
|
|
846
1121
|
async redirectToAuthorization(authUrl) {
|
|
847
1122
|
this._authUrl = authUrl.toString();
|
|
@@ -853,7 +1128,6 @@ var StorageOAuthClientProvider = class {
|
|
|
853
1128
|
if (scope === "all") {
|
|
854
1129
|
await storage.removeSession(this.identity, this.sessionId);
|
|
855
1130
|
} else {
|
|
856
|
-
await this.getSessionData();
|
|
857
1131
|
const updates = {};
|
|
858
1132
|
if (scope === "client") {
|
|
859
1133
|
updates.clientInformation = void 0;
|
|
@@ -1173,41 +1447,33 @@ var MCPClient = class _MCPClient {
|
|
|
1173
1447
|
if (!this.serverUrl || !this.callbackUrl || !this.serverId) {
|
|
1174
1448
|
throw new Error("Missing required connection metadata");
|
|
1175
1449
|
}
|
|
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
1450
|
if (!this.oauthProvider) {
|
|
1187
1451
|
if (!this.serverId) {
|
|
1188
1452
|
throw new Error("serverId required for OAuth provider initialization");
|
|
1189
1453
|
}
|
|
1190
|
-
this.oauthProvider = new StorageOAuthClientProvider(
|
|
1191
|
-
this.identity,
|
|
1192
|
-
this.serverId,
|
|
1193
|
-
this.sessionId,
|
|
1194
|
-
|
|
1195
|
-
this.
|
|
1196
|
-
|
|
1454
|
+
this.oauthProvider = new StorageOAuthClientProvider({
|
|
1455
|
+
identity: this.identity,
|
|
1456
|
+
serverId: this.serverId,
|
|
1457
|
+
sessionId: this.sessionId,
|
|
1458
|
+
redirectUrl: this.callbackUrl,
|
|
1459
|
+
clientName: this.clientName,
|
|
1460
|
+
clientUri: this.clientUri,
|
|
1461
|
+
logoUri: this.logoUri,
|
|
1462
|
+
policyUri: this.policyUri,
|
|
1463
|
+
clientId: this.clientId,
|
|
1464
|
+
clientSecret: this.clientSecret,
|
|
1465
|
+
onRedirect: (redirectUrl) => {
|
|
1197
1466
|
if (this.onRedirect) {
|
|
1198
1467
|
this.onRedirect(redirectUrl);
|
|
1199
1468
|
}
|
|
1200
1469
|
}
|
|
1201
|
-
);
|
|
1202
|
-
if (this.clientId && this.oauthProvider) {
|
|
1203
|
-
this.oauthProvider.clientId = this.clientId;
|
|
1204
|
-
}
|
|
1470
|
+
});
|
|
1205
1471
|
}
|
|
1206
1472
|
if (!this.client) {
|
|
1207
1473
|
this.client = new Client(
|
|
1208
1474
|
{
|
|
1209
|
-
name:
|
|
1210
|
-
version:
|
|
1475
|
+
name: MCP_CLIENT_NAME,
|
|
1476
|
+
version: MCP_CLIENT_VERSION
|
|
1211
1477
|
},
|
|
1212
1478
|
{
|
|
1213
1479
|
capabilities: {
|
|
@@ -1402,8 +1668,8 @@ var MCPClient = class _MCPClient {
|
|
|
1402
1668
|
this.emitProgress("Creating authenticated client...");
|
|
1403
1669
|
this.client = new Client(
|
|
1404
1670
|
{
|
|
1405
|
-
name:
|
|
1406
|
-
version:
|
|
1671
|
+
name: MCP_CLIENT_NAME,
|
|
1672
|
+
version: MCP_CLIENT_VERSION
|
|
1407
1673
|
},
|
|
1408
1674
|
{
|
|
1409
1675
|
capabilities: {
|
|
@@ -1698,8 +1964,8 @@ var MCPClient = class _MCPClient {
|
|
|
1698
1964
|
}
|
|
1699
1965
|
this.client = new Client(
|
|
1700
1966
|
{
|
|
1701
|
-
name:
|
|
1702
|
-
version:
|
|
1967
|
+
name: MCP_CLIENT_NAME,
|
|
1968
|
+
version: MCP_CLIENT_VERSION
|
|
1703
1969
|
},
|
|
1704
1970
|
{ capabilities: {} }
|
|
1705
1971
|
);
|
|
@@ -1968,6 +2234,27 @@ var MultiSessionClient = class {
|
|
|
1968
2234
|
}
|
|
1969
2235
|
};
|
|
1970
2236
|
|
|
2237
|
+
// src/shared/event-routing.ts
|
|
2238
|
+
function isRpcResponseEvent(event) {
|
|
2239
|
+
return "id" in event && ("result" in event || "error" in event);
|
|
2240
|
+
}
|
|
2241
|
+
function isConnectionEvent(event) {
|
|
2242
|
+
if (!("type" in event)) {
|
|
2243
|
+
return false;
|
|
2244
|
+
}
|
|
2245
|
+
switch (event.type) {
|
|
2246
|
+
case "state_changed":
|
|
2247
|
+
case "tools_discovered":
|
|
2248
|
+
case "auth_required":
|
|
2249
|
+
case "error":
|
|
2250
|
+
case "disconnected":
|
|
2251
|
+
case "progress":
|
|
2252
|
+
return true;
|
|
2253
|
+
default:
|
|
2254
|
+
return false;
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
|
|
1971
2258
|
// src/server/handlers/sse-handler.ts
|
|
1972
2259
|
var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
|
|
1973
2260
|
var SSEConnectionManager = class {
|
|
@@ -2110,16 +2397,6 @@ var SSEConnectionManager = class {
|
|
|
2110
2397
|
throw new Error(`Connection already exists for server: ${duplicate.serverUrl || duplicate.serverId} (${duplicate.serverName})`);
|
|
2111
2398
|
}
|
|
2112
2399
|
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
2400
|
try {
|
|
2124
2401
|
const clientMetadata = await this.getResolvedClientMetadata();
|
|
2125
2402
|
const client = new MCPClient({
|
|
@@ -2130,17 +2407,8 @@ var SSEConnectionManager = class {
|
|
|
2130
2407
|
serverUrl,
|
|
2131
2408
|
callbackUrl,
|
|
2132
2409
|
transportType,
|
|
2133
|
-
...clientMetadata
|
|
2410
|
+
...clientMetadata
|
|
2134
2411
|
// 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
2412
|
});
|
|
2145
2413
|
this.clients.set(sessionId, client);
|
|
2146
2414
|
client.onConnectionEvent((event) => {
|
|
@@ -2150,20 +2418,19 @@ var SSEConnectionManager = class {
|
|
|
2150
2418
|
this.sendEvent(event);
|
|
2151
2419
|
});
|
|
2152
2420
|
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
|
-
});
|
|
2421
|
+
await client.listTools();
|
|
2162
2422
|
return {
|
|
2163
2423
|
sessionId,
|
|
2164
2424
|
success: true
|
|
2165
2425
|
};
|
|
2166
2426
|
} catch (error) {
|
|
2427
|
+
if (error instanceof UnauthorizedError) {
|
|
2428
|
+
this.clients.delete(sessionId);
|
|
2429
|
+
return {
|
|
2430
|
+
sessionId,
|
|
2431
|
+
success: true
|
|
2432
|
+
};
|
|
2433
|
+
}
|
|
2167
2434
|
this.emitConnectionEvent({
|
|
2168
2435
|
type: "error",
|
|
2169
2436
|
sessionId,
|
|
@@ -2265,14 +2532,6 @@ var SSEConnectionManager = class {
|
|
|
2265
2532
|
await client.connect();
|
|
2266
2533
|
this.clients.set(sessionId, client);
|
|
2267
2534
|
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
2535
|
return { success: true, toolCount: tools.tools.length };
|
|
2277
2536
|
} catch (error) {
|
|
2278
2537
|
this.emitConnectionEvent({
|
|
@@ -2295,16 +2554,6 @@ var SSEConnectionManager = class {
|
|
|
2295
2554
|
if (!session) {
|
|
2296
2555
|
throw new Error("Session not found");
|
|
2297
2556
|
}
|
|
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
2557
|
try {
|
|
2309
2558
|
const client = new MCPClient({
|
|
2310
2559
|
identity: this.identity,
|
|
@@ -2314,14 +2563,6 @@ var SSEConnectionManager = class {
|
|
|
2314
2563
|
await client.finishAuth(code);
|
|
2315
2564
|
this.clients.set(sessionId, client);
|
|
2316
2565
|
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
2566
|
return { success: true, toolCount: tools.tools.length };
|
|
2326
2567
|
} catch (error) {
|
|
2327
2568
|
this.emitConnectionEvent({
|
|
@@ -2399,9 +2640,9 @@ function createSSEHandler(options) {
|
|
|
2399
2640
|
});
|
|
2400
2641
|
writeSSEEvent(res, "connected", { timestamp: Date.now() });
|
|
2401
2642
|
const manager = new SSEConnectionManager(options, (event) => {
|
|
2402
|
-
if (
|
|
2643
|
+
if (isRpcResponseEvent(event)) {
|
|
2403
2644
|
writeSSEEvent(res, "rpc-response", event);
|
|
2404
|
-
} else if (
|
|
2645
|
+
} else if (isConnectionEvent(event)) {
|
|
2405
2646
|
writeSSEEvent(res, "connection", event);
|
|
2406
2647
|
} else {
|
|
2407
2648
|
writeSSEEvent(res, "observability", event);
|
|
@@ -2432,9 +2673,6 @@ function writeSSEEvent(res, event, data) {
|
|
|
2432
2673
|
}
|
|
2433
2674
|
|
|
2434
2675
|
// src/server/handlers/nextjs-handler.ts
|
|
2435
|
-
function isRpcResponseEvent(event) {
|
|
2436
|
-
return "id" in event && ("result" in event || "error" in event);
|
|
2437
|
-
}
|
|
2438
2676
|
function createNextMcpHandler(options = {}) {
|
|
2439
2677
|
const {
|
|
2440
2678
|
getIdentity = (request) => new URL(request.url).searchParams.get("identity"),
|
|
@@ -2525,7 +2763,7 @@ data: ${JSON.stringify(data)}
|
|
|
2525
2763
|
(event) => {
|
|
2526
2764
|
if (isRpcResponseEvent(event)) {
|
|
2527
2765
|
sendSSE("rpc-response", event);
|
|
2528
|
-
} else if (
|
|
2766
|
+
} else if (isConnectionEvent(event)) {
|
|
2529
2767
|
sendSSE("connection", event);
|
|
2530
2768
|
} else {
|
|
2531
2769
|
sendSSE("observability", event);
|