@mcp-ts/sdk 1.0.1 → 1.2.0
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 +42 -19
- package/dist/adapters/agui-adapter.d.mts +6 -6
- package/dist/adapters/agui-adapter.d.ts +6 -6
- package/dist/adapters/agui-adapter.js +50 -25
- package/dist/adapters/agui-adapter.js.map +1 -1
- package/dist/adapters/agui-adapter.mjs +50 -25
- package/dist/adapters/agui-adapter.mjs.map +1 -1
- package/dist/adapters/agui-middleware.d.mts +23 -8
- package/dist/adapters/agui-middleware.d.ts +23 -8
- package/dist/adapters/agui-middleware.js +71 -43
- package/dist/adapters/agui-middleware.js.map +1 -1
- package/dist/adapters/agui-middleware.mjs +71 -44
- package/dist/adapters/agui-middleware.mjs.map +1 -1
- package/dist/adapters/ai-adapter.d.mts +2 -2
- package/dist/adapters/ai-adapter.d.ts +2 -2
- package/dist/adapters/langchain-adapter.d.mts +2 -2
- package/dist/adapters/langchain-adapter.d.ts +2 -2
- package/dist/adapters/mastra-adapter.d.mts +2 -2
- package/dist/adapters/mastra-adapter.d.ts +2 -2
- package/dist/client/index.d.mts +182 -55
- package/dist/client/index.d.ts +182 -55
- package/dist/client/index.js +535 -130
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +535 -131
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +386 -4
- package/dist/client/react.d.ts +386 -4
- package/dist/client/react.js +890 -143
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +883 -145
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +3 -3
- package/dist/client/vue.d.ts +3 -3
- package/dist/client/vue.js +546 -141
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +546 -142
- package/dist/client/vue.mjs.map +1 -1
- package/dist/{events-BP6WyRNh.d.mts → events-BgeztGYZ.d.mts} +12 -1
- package/dist/{events-BP6WyRNh.d.ts → events-BgeztGYZ.d.ts} +12 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +797 -248
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +791 -245
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-DMF3ED2O.d.mts → multi-session-client-CxogNckF.d.mts} +1 -1
- package/dist/{multi-session-client-BOFgPypS.d.ts → multi-session-client-cox_WXUj.d.ts} +1 -1
- package/dist/server/index.d.mts +41 -37
- package/dist/server/index.d.ts +41 -37
- package/dist/server/index.js +241 -116
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +237 -112
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +39 -3
- package/dist/shared/index.d.ts +39 -3
- package/dist/shared/index.js +19 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +18 -1
- package/dist/shared/index.mjs.map +1 -1
- package/package.json +9 -2
- package/src/adapters/agui-adapter.ts +58 -35
- package/src/adapters/agui-middleware.ts +83 -45
- package/src/client/core/app-host.ts +417 -0
- package/src/client/core/sse-client.ts +365 -212
- package/src/client/core/types.ts +31 -0
- package/src/client/index.ts +1 -0
- package/src/client/react/agui-subscriber.ts +275 -0
- package/src/client/react/index.ts +23 -3
- package/src/client/react/use-agui-subscriber.ts +270 -0
- package/src/client/react/use-app-host.ts +73 -0
- package/src/client/react/use-mcp-app-iframe.ts +164 -0
- package/src/client/react/{useMcp.ts → use-mcp.ts} +18 -0
- package/src/client/vue/index.ts +1 -1
- package/src/server/handlers/nextjs-handler.ts +8 -7
- package/src/server/handlers/sse-handler.ts +129 -165
- package/src/server/mcp/oauth-client.ts +32 -2
- package/src/server/storage/index.ts +17 -1
- package/src/server/storage/sqlite-backend.ts +185 -0
- package/src/shared/events.ts +12 -0
- package/src/shared/index.ts +6 -1
- package/src/shared/tool-utils.ts +61 -0
- package/src/shared/types.ts +3 -1
- /package/src/client/vue/{useMcp.ts → use-mcp.ts} +0 -0
package/dist/server/index.js
CHANGED
|
@@ -6,7 +6,7 @@ var streamableHttp_js = require('@modelcontextprotocol/sdk/client/streamableHttp
|
|
|
6
6
|
var sse_js = require('@modelcontextprotocol/sdk/client/sse.js');
|
|
7
7
|
var auth_js = require('@modelcontextprotocol/sdk/client/auth.js');
|
|
8
8
|
var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
9
|
-
var
|
|
9
|
+
var fs2 = require('fs');
|
|
10
10
|
var path = require('path');
|
|
11
11
|
|
|
12
12
|
function _interopNamespace(e) {
|
|
@@ -27,6 +27,7 @@ function _interopNamespace(e) {
|
|
|
27
27
|
return Object.freeze(n);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
var fs2__namespace = /*#__PURE__*/_interopNamespace(fs2);
|
|
30
31
|
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
31
32
|
|
|
32
33
|
var __defProp = Object.defineProperty;
|
|
@@ -424,8 +425,8 @@ var FileStorageBackend = class {
|
|
|
424
425
|
if (this.initialized) return;
|
|
425
426
|
try {
|
|
426
427
|
const dir = path__namespace.dirname(this.filePath);
|
|
427
|
-
await
|
|
428
|
-
const data = await
|
|
428
|
+
await fs2.promises.mkdir(dir, { recursive: true });
|
|
429
|
+
const data = await fs2.promises.readFile(this.filePath, "utf-8");
|
|
429
430
|
const json = JSON.parse(data);
|
|
430
431
|
this.memoryCache = /* @__PURE__ */ new Map();
|
|
431
432
|
if (Array.isArray(json)) {
|
|
@@ -450,7 +451,7 @@ var FileStorageBackend = class {
|
|
|
450
451
|
async flush() {
|
|
451
452
|
if (!this.memoryCache) return;
|
|
452
453
|
const sessions = Array.from(this.memoryCache.values());
|
|
453
|
-
await
|
|
454
|
+
await fs2.promises.writeFile(this.filePath, JSON.stringify(sessions, null, 2), "utf-8");
|
|
454
455
|
}
|
|
455
456
|
getSessionKey(identity, sessionId) {
|
|
456
457
|
return `${identity}:${sessionId}`;
|
|
@@ -519,6 +520,148 @@ var FileStorageBackend = class {
|
|
|
519
520
|
async disconnect() {
|
|
520
521
|
}
|
|
521
522
|
};
|
|
523
|
+
var SqliteStorage = class {
|
|
524
|
+
constructor(options = {}) {
|
|
525
|
+
__publicField(this, "db", null);
|
|
526
|
+
__publicField(this, "table");
|
|
527
|
+
__publicField(this, "initialized", false);
|
|
528
|
+
__publicField(this, "dbPath");
|
|
529
|
+
this.dbPath = options.path || "./sessions.db";
|
|
530
|
+
this.table = options.table || "mcp_sessions";
|
|
531
|
+
}
|
|
532
|
+
async init() {
|
|
533
|
+
if (this.initialized) return;
|
|
534
|
+
try {
|
|
535
|
+
const DatabaseConstructor = (await import('better-sqlite3')).default;
|
|
536
|
+
const dir = path__namespace.dirname(this.dbPath);
|
|
537
|
+
if (!fs2__namespace.existsSync(dir)) {
|
|
538
|
+
fs2__namespace.mkdirSync(dir, { recursive: true });
|
|
539
|
+
}
|
|
540
|
+
this.db = new DatabaseConstructor(this.dbPath);
|
|
541
|
+
this.db.exec(`
|
|
542
|
+
CREATE TABLE IF NOT EXISTS ${this.table} (
|
|
543
|
+
sessionId TEXT PRIMARY KEY,
|
|
544
|
+
identity TEXT NOT NULL,
|
|
545
|
+
data TEXT NOT NULL,
|
|
546
|
+
expiresAt INTEGER
|
|
547
|
+
);
|
|
548
|
+
CREATE INDEX IF NOT EXISTS idx_${this.table}_identity ON ${this.table}(identity);
|
|
549
|
+
`);
|
|
550
|
+
this.initialized = true;
|
|
551
|
+
} catch (error) {
|
|
552
|
+
if (error.code === "MODULE_NOT_FOUND" || error.message?.includes("better-sqlite3")) {
|
|
553
|
+
throw new Error(
|
|
554
|
+
"better-sqlite3 is not installed. Please install it with: npm install better-sqlite3"
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
throw error;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
ensureInitialized() {
|
|
561
|
+
if (!this.initialized) {
|
|
562
|
+
throw new Error("SqliteStorage not initialized. Call init() first.");
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
generateSessionId() {
|
|
566
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
567
|
+
let result = "";
|
|
568
|
+
for (let i = 0; i < 32; i++) {
|
|
569
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
570
|
+
}
|
|
571
|
+
return result;
|
|
572
|
+
}
|
|
573
|
+
async createSession(session, ttl) {
|
|
574
|
+
this.ensureInitialized();
|
|
575
|
+
const { sessionId, identity } = session;
|
|
576
|
+
if (!sessionId || !identity) {
|
|
577
|
+
throw new Error("identity and sessionId required");
|
|
578
|
+
}
|
|
579
|
+
const expiresAt = ttl ? Date.now() + ttl * 1e3 : null;
|
|
580
|
+
try {
|
|
581
|
+
const stmt = this.db.prepare(
|
|
582
|
+
`INSERT INTO ${this.table} (sessionId, identity, data, expiresAt) VALUES (?, ?, ?, ?)`
|
|
583
|
+
);
|
|
584
|
+
stmt.run(sessionId, identity, JSON.stringify(session), expiresAt);
|
|
585
|
+
} catch (error) {
|
|
586
|
+
if (error.code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
|
|
587
|
+
throw new Error(`Session ${sessionId} already exists`);
|
|
588
|
+
}
|
|
589
|
+
throw error;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
async updateSession(identity, sessionId, data, ttl) {
|
|
593
|
+
this.ensureInitialized();
|
|
594
|
+
if (!sessionId || !identity) {
|
|
595
|
+
throw new Error("identity and sessionId required");
|
|
596
|
+
}
|
|
597
|
+
const currentSession = await this.getSession(identity, sessionId);
|
|
598
|
+
if (!currentSession) {
|
|
599
|
+
throw new Error(`Session ${sessionId} not found for identity ${identity}`);
|
|
600
|
+
}
|
|
601
|
+
const updatedSession = { ...currentSession, ...data };
|
|
602
|
+
const expiresAt = ttl ? Date.now() + ttl * 1e3 : null;
|
|
603
|
+
const stmt = this.db.prepare(
|
|
604
|
+
`UPDATE ${this.table} SET data = ?, expiresAt = ? WHERE sessionId = ? AND identity = ?`
|
|
605
|
+
);
|
|
606
|
+
stmt.run(JSON.stringify(updatedSession), expiresAt, sessionId, identity);
|
|
607
|
+
}
|
|
608
|
+
async getSession(identity, sessionId) {
|
|
609
|
+
this.ensureInitialized();
|
|
610
|
+
const stmt = this.db.prepare(
|
|
611
|
+
`SELECT data FROM ${this.table} WHERE sessionId = ? AND identity = ?`
|
|
612
|
+
);
|
|
613
|
+
const row = stmt.get(sessionId, identity);
|
|
614
|
+
if (!row) return null;
|
|
615
|
+
return JSON.parse(row.data);
|
|
616
|
+
}
|
|
617
|
+
async getIdentitySessionsData(identity) {
|
|
618
|
+
this.ensureInitialized();
|
|
619
|
+
const stmt = this.db.prepare(
|
|
620
|
+
`SELECT data FROM ${this.table} WHERE identity = ?`
|
|
621
|
+
);
|
|
622
|
+
const rows = stmt.all(identity);
|
|
623
|
+
return rows.map((row) => JSON.parse(row.data));
|
|
624
|
+
}
|
|
625
|
+
async getIdentityMcpSessions(identity) {
|
|
626
|
+
this.ensureInitialized();
|
|
627
|
+
const stmt = this.db.prepare(
|
|
628
|
+
`SELECT sessionId FROM ${this.table} WHERE identity = ?`
|
|
629
|
+
);
|
|
630
|
+
const rows = stmt.all(identity);
|
|
631
|
+
return rows.map((row) => row.sessionId);
|
|
632
|
+
}
|
|
633
|
+
async removeSession(identity, sessionId) {
|
|
634
|
+
this.ensureInitialized();
|
|
635
|
+
const stmt = this.db.prepare(
|
|
636
|
+
`DELETE FROM ${this.table} WHERE sessionId = ? AND identity = ?`
|
|
637
|
+
);
|
|
638
|
+
stmt.run(sessionId, identity);
|
|
639
|
+
}
|
|
640
|
+
async getAllSessionIds() {
|
|
641
|
+
this.ensureInitialized();
|
|
642
|
+
const stmt = this.db.prepare(`SELECT sessionId FROM ${this.table}`);
|
|
643
|
+
const rows = stmt.all();
|
|
644
|
+
return rows.map((row) => row.sessionId);
|
|
645
|
+
}
|
|
646
|
+
async clearAll() {
|
|
647
|
+
this.ensureInitialized();
|
|
648
|
+
const stmt = this.db.prepare(`DELETE FROM ${this.table}`);
|
|
649
|
+
stmt.run();
|
|
650
|
+
}
|
|
651
|
+
async cleanupExpiredSessions() {
|
|
652
|
+
this.ensureInitialized();
|
|
653
|
+
const now = Date.now();
|
|
654
|
+
const stmt = this.db.prepare(
|
|
655
|
+
`DELETE FROM ${this.table} WHERE expiresAt IS NOT NULL AND expiresAt < ?`
|
|
656
|
+
);
|
|
657
|
+
stmt.run(now);
|
|
658
|
+
}
|
|
659
|
+
async disconnect() {
|
|
660
|
+
if (this.db) {
|
|
661
|
+
this.db.close();
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
};
|
|
522
665
|
|
|
523
666
|
// src/server/storage/index.ts
|
|
524
667
|
var storageInstance = null;
|
|
@@ -550,6 +693,13 @@ async function createStorage() {
|
|
|
550
693
|
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
551
694
|
return store;
|
|
552
695
|
}
|
|
696
|
+
if (type === "sqlite") {
|
|
697
|
+
const dbPath = process.env.MCP_TS_STORAGE_SQLITE_PATH;
|
|
698
|
+
console.log(`[Storage] Using SQLite storage (${dbPath || "default"}) (Explicit)`);
|
|
699
|
+
const store = new SqliteStorage({ path: dbPath });
|
|
700
|
+
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
701
|
+
return store;
|
|
702
|
+
}
|
|
553
703
|
if (type === "memory") {
|
|
554
704
|
console.log("[Storage] Using In-Memory storage (Explicit)");
|
|
555
705
|
return new MemoryStorageBackend();
|
|
@@ -572,6 +722,12 @@ async function createStorage() {
|
|
|
572
722
|
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
573
723
|
return store;
|
|
574
724
|
}
|
|
725
|
+
if (process.env.MCP_TS_STORAGE_SQLITE_PATH) {
|
|
726
|
+
console.log(`[Storage] Auto-detected MCP_TS_STORAGE_SQLITE_PATH. Using SQLite storage (${process.env.MCP_TS_STORAGE_SQLITE_PATH}).`);
|
|
727
|
+
const store = new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH });
|
|
728
|
+
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
729
|
+
return store;
|
|
730
|
+
}
|
|
575
731
|
console.log("[Storage] No storage configured. Using In-Memory storage (Default).");
|
|
576
732
|
return new MemoryStorageBackend();
|
|
577
733
|
}
|
|
@@ -1079,7 +1235,15 @@ var MCPClient = class _MCPClient {
|
|
|
1079
1235
|
name: "mcp-ts-oauth-client",
|
|
1080
1236
|
version: "2.0"
|
|
1081
1237
|
},
|
|
1082
|
-
{
|
|
1238
|
+
{
|
|
1239
|
+
capabilities: {
|
|
1240
|
+
extensions: {
|
|
1241
|
+
"io.modelcontextprotocol/ui": {
|
|
1242
|
+
mimeTypes: ["text/html+mcp"]
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1083
1247
|
);
|
|
1084
1248
|
}
|
|
1085
1249
|
const existingSession = await storage.getSession(this.identity, this.sessionId);
|
|
@@ -1261,7 +1425,15 @@ var MCPClient = class _MCPClient {
|
|
|
1261
1425
|
name: "mcp-ts-oauth-client",
|
|
1262
1426
|
version: "2.0"
|
|
1263
1427
|
},
|
|
1264
|
-
{
|
|
1428
|
+
{
|
|
1429
|
+
capabilities: {
|
|
1430
|
+
extensions: {
|
|
1431
|
+
"io.modelcontextprotocol/ui": {
|
|
1432
|
+
mimeTypes: ["text/html+mcp"]
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1265
1437
|
);
|
|
1266
1438
|
this.emitStateChange("CONNECTING");
|
|
1267
1439
|
await this.client.connect(this.transport);
|
|
@@ -1817,6 +1989,7 @@ var MultiSessionClient = class {
|
|
|
1817
1989
|
};
|
|
1818
1990
|
|
|
1819
1991
|
// src/server/handlers/sse-handler.ts
|
|
1992
|
+
var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
|
|
1820
1993
|
var SSEConnectionManager = class {
|
|
1821
1994
|
constructor(options, sendEvent) {
|
|
1822
1995
|
this.options = options;
|
|
@@ -1846,7 +2019,7 @@ var SSEConnectionManager = class {
|
|
|
1846
2019
|
* Start heartbeat to keep connection alive
|
|
1847
2020
|
*/
|
|
1848
2021
|
startHeartbeat() {
|
|
1849
|
-
const interval = this.options.heartbeatInterval
|
|
2022
|
+
const interval = this.options.heartbeatInterval ?? DEFAULT_HEARTBEAT_INTERVAL;
|
|
1850
2023
|
this.heartbeatTimer = setInterval(() => {
|
|
1851
2024
|
if (this.isActive) {
|
|
1852
2025
|
this.sendEvent({
|
|
@@ -1859,6 +2032,7 @@ var SSEConnectionManager = class {
|
|
|
1859
2032
|
}
|
|
1860
2033
|
/**
|
|
1861
2034
|
* Handle incoming RPC requests
|
|
2035
|
+
* Returns the RPC response directly for immediate HTTP response (bypassing SSE latency)
|
|
1862
2036
|
*/
|
|
1863
2037
|
async handleRequest(request) {
|
|
1864
2038
|
try {
|
|
@@ -1900,39 +2074,29 @@ var SSEConnectionManager = class {
|
|
|
1900
2074
|
default:
|
|
1901
2075
|
throw new Error(`Unknown method: ${request.method}`);
|
|
1902
2076
|
}
|
|
1903
|
-
|
|
2077
|
+
const response = {
|
|
1904
2078
|
id: request.id,
|
|
1905
2079
|
result
|
|
1906
|
-
}
|
|
2080
|
+
};
|
|
2081
|
+
this.sendEvent(response);
|
|
2082
|
+
return response;
|
|
1907
2083
|
} catch (error) {
|
|
1908
|
-
|
|
2084
|
+
const errorResponse = {
|
|
1909
2085
|
id: request.id,
|
|
1910
2086
|
error: {
|
|
1911
2087
|
code: RpcErrorCodes.EXECUTION_ERROR,
|
|
1912
2088
|
message: error instanceof Error ? error.message : "Unknown error"
|
|
1913
2089
|
}
|
|
1914
|
-
}
|
|
2090
|
+
};
|
|
2091
|
+
this.sendEvent(errorResponse);
|
|
2092
|
+
return errorResponse;
|
|
1915
2093
|
}
|
|
1916
2094
|
}
|
|
1917
2095
|
/**
|
|
1918
|
-
* Get all
|
|
2096
|
+
* Get all sessions for the current identity
|
|
1919
2097
|
*/
|
|
1920
2098
|
async getSessions() {
|
|
1921
2099
|
const sessions = await storage.getIdentitySessionsData(this.identity);
|
|
1922
|
-
this.sendEvent({
|
|
1923
|
-
level: "debug",
|
|
1924
|
-
message: `Retrieved ${sessions.length} sessions for identity ${this.identity}`,
|
|
1925
|
-
timestamp: Date.now(),
|
|
1926
|
-
metadata: {
|
|
1927
|
-
identity: this.identity,
|
|
1928
|
-
sessionCount: sessions.length,
|
|
1929
|
-
sessions: sessions.map((s) => ({
|
|
1930
|
-
sessionId: s.sessionId,
|
|
1931
|
-
serverId: s.serverId,
|
|
1932
|
-
serverName: s.serverName
|
|
1933
|
-
}))
|
|
1934
|
-
}
|
|
1935
|
-
});
|
|
1936
2100
|
return {
|
|
1937
2101
|
sessions: sessions.map((s) => ({
|
|
1938
2102
|
sessionId: s.sessionId,
|
|
@@ -1948,7 +2112,7 @@ var SSEConnectionManager = class {
|
|
|
1948
2112
|
*/
|
|
1949
2113
|
async connect(params) {
|
|
1950
2114
|
const { serverName, serverUrl, callbackUrl, transportType } = params;
|
|
1951
|
-
const serverId = params.serverId
|
|
2115
|
+
const serverId = params.serverId && params.serverId.length <= 12 ? params.serverId : await storage.generateSessionId();
|
|
1952
2116
|
const existingSessions = await storage.getIdentitySessionsData(this.identity);
|
|
1953
2117
|
const duplicate = existingSessions.find(
|
|
1954
2118
|
(s) => s.serverId === serverId || s.serverUrl === serverUrl
|
|
@@ -1997,10 +2161,6 @@ var SSEConnectionManager = class {
|
|
|
1997
2161
|
});
|
|
1998
2162
|
await client.connect();
|
|
1999
2163
|
const tools = await client.listTools();
|
|
2000
|
-
const sessionAfterConnect = await storage.getSession(this.identity, sessionId);
|
|
2001
|
-
console.log(`[SSE Handler] After connect() - Session ${sessionId}:`, {
|
|
2002
|
-
serverId: sessionAfterConnect?.serverId
|
|
2003
|
-
});
|
|
2004
2164
|
this.emitConnectionEvent({
|
|
2005
2165
|
type: "tools_discovered",
|
|
2006
2166
|
sessionId,
|
|
@@ -2042,24 +2202,21 @@ var SSEConnectionManager = class {
|
|
|
2042
2202
|
return { success: true };
|
|
2043
2203
|
}
|
|
2044
2204
|
/**
|
|
2045
|
-
*
|
|
2205
|
+
* Get an existing client or create and connect a new one for the session.
|
|
2046
2206
|
*/
|
|
2047
2207
|
async getOrCreateClient(sessionId) {
|
|
2048
|
-
|
|
2049
|
-
if (
|
|
2050
|
-
|
|
2051
|
-
identity: this.identity,
|
|
2052
|
-
sessionId
|
|
2053
|
-
});
|
|
2054
|
-
client.onConnectionEvent((event) => {
|
|
2055
|
-
this.emitConnectionEvent(event);
|
|
2056
|
-
});
|
|
2057
|
-
client.onObservabilityEvent((event) => {
|
|
2058
|
-
this.sendEvent(event);
|
|
2059
|
-
});
|
|
2060
|
-
await client.connect();
|
|
2061
|
-
this.clients.set(sessionId, client);
|
|
2208
|
+
const existing = this.clients.get(sessionId);
|
|
2209
|
+
if (existing) {
|
|
2210
|
+
return existing;
|
|
2062
2211
|
}
|
|
2212
|
+
const client = new MCPClient({
|
|
2213
|
+
identity: this.identity,
|
|
2214
|
+
sessionId
|
|
2215
|
+
});
|
|
2216
|
+
client.onConnectionEvent((event) => this.emitConnectionEvent(event));
|
|
2217
|
+
client.onObservabilityEvent((event) => this.sendEvent(event));
|
|
2218
|
+
await client.connect();
|
|
2219
|
+
this.clients.set(sessionId, client);
|
|
2063
2220
|
return client;
|
|
2064
2221
|
}
|
|
2065
2222
|
/**
|
|
@@ -2072,51 +2229,35 @@ var SSEConnectionManager = class {
|
|
|
2072
2229
|
return { tools: result.tools };
|
|
2073
2230
|
}
|
|
2074
2231
|
/**
|
|
2075
|
-
* Call a tool
|
|
2232
|
+
* Call a tool on the MCP server
|
|
2076
2233
|
*/
|
|
2077
2234
|
async callTool(params) {
|
|
2078
2235
|
const { sessionId, toolName, toolArgs } = params;
|
|
2079
2236
|
const client = await this.getOrCreateClient(sessionId);
|
|
2080
|
-
|
|
2237
|
+
const result = await client.callTool(toolName, toolArgs);
|
|
2238
|
+
const meta = result._meta || {};
|
|
2239
|
+
return {
|
|
2240
|
+
...result,
|
|
2241
|
+
_meta: {
|
|
2242
|
+
...meta,
|
|
2243
|
+
sessionId
|
|
2244
|
+
}
|
|
2245
|
+
};
|
|
2081
2246
|
}
|
|
2082
2247
|
/**
|
|
2083
|
-
*
|
|
2248
|
+
* Restore and validate an existing session
|
|
2084
2249
|
*/
|
|
2085
2250
|
async restoreSession(params) {
|
|
2086
2251
|
const { sessionId } = params;
|
|
2087
|
-
this.sendEvent({
|
|
2088
|
-
level: "debug",
|
|
2089
|
-
message: `Starting session refresh for ${sessionId}`,
|
|
2090
|
-
timestamp: Date.now(),
|
|
2091
|
-
metadata: { sessionId, identity: this.identity }
|
|
2092
|
-
});
|
|
2093
2252
|
const session = await storage.getSession(this.identity, sessionId);
|
|
2094
2253
|
if (!session) {
|
|
2095
|
-
this.sendEvent({
|
|
2096
|
-
level: "error",
|
|
2097
|
-
message: `Session not found: ${sessionId}`,
|
|
2098
|
-
timestamp: Date.now(),
|
|
2099
|
-
metadata: { sessionId, identity: this.identity }
|
|
2100
|
-
});
|
|
2101
2254
|
throw new Error("Session not found");
|
|
2102
2255
|
}
|
|
2103
|
-
this.sendEvent({
|
|
2104
|
-
level: "debug",
|
|
2105
|
-
message: `Session found in Redis`,
|
|
2106
|
-
timestamp: Date.now(),
|
|
2107
|
-
metadata: {
|
|
2108
|
-
sessionId,
|
|
2109
|
-
serverId: session.serverId,
|
|
2110
|
-
serverName: session.serverName,
|
|
2111
|
-
serverUrl: session.serverUrl,
|
|
2112
|
-
transportType: session.transportType
|
|
2113
|
-
}
|
|
2114
|
-
});
|
|
2115
2256
|
this.emitConnectionEvent({
|
|
2116
2257
|
type: "state_changed",
|
|
2117
2258
|
sessionId,
|
|
2118
|
-
serverId: session.serverId
|
|
2119
|
-
serverName: session.serverName
|
|
2259
|
+
serverId: session.serverId ?? "unknown",
|
|
2260
|
+
serverName: session.serverName ?? "Unknown",
|
|
2120
2261
|
state: "VALIDATING",
|
|
2121
2262
|
previousState: "DISCONNECTED",
|
|
2122
2263
|
timestamp: Date.now()
|
|
@@ -2127,21 +2268,16 @@ var SSEConnectionManager = class {
|
|
|
2127
2268
|
identity: this.identity,
|
|
2128
2269
|
sessionId,
|
|
2129
2270
|
...clientMetadata
|
|
2130
|
-
// Include metadata for consistency
|
|
2131
|
-
});
|
|
2132
|
-
client.onConnectionEvent((event) => {
|
|
2133
|
-
this.emitConnectionEvent(event);
|
|
2134
|
-
});
|
|
2135
|
-
client.onObservabilityEvent((event) => {
|
|
2136
|
-
this.sendEvent(event);
|
|
2137
2271
|
});
|
|
2272
|
+
client.onConnectionEvent((event) => this.emitConnectionEvent(event));
|
|
2273
|
+
client.onObservabilityEvent((event) => this.sendEvent(event));
|
|
2138
2274
|
await client.connect();
|
|
2139
2275
|
this.clients.set(sessionId, client);
|
|
2140
2276
|
const tools = await client.listTools();
|
|
2141
2277
|
this.emitConnectionEvent({
|
|
2142
2278
|
type: "tools_discovered",
|
|
2143
2279
|
sessionId,
|
|
2144
|
-
serverId: session.serverId
|
|
2280
|
+
serverId: session.serverId ?? "unknown",
|
|
2145
2281
|
toolCount: tools.tools.length,
|
|
2146
2282
|
tools: tools.tools,
|
|
2147
2283
|
timestamp: Date.now()
|
|
@@ -2151,7 +2287,7 @@ var SSEConnectionManager = class {
|
|
|
2151
2287
|
this.emitConnectionEvent({
|
|
2152
2288
|
type: "error",
|
|
2153
2289
|
sessionId,
|
|
2154
|
-
serverId: session.serverId
|
|
2290
|
+
serverId: session.serverId ?? "unknown",
|
|
2155
2291
|
error: error instanceof Error ? error.message : "Validation failed",
|
|
2156
2292
|
errorType: "validation",
|
|
2157
2293
|
timestamp: Date.now()
|
|
@@ -2160,16 +2296,10 @@ var SSEConnectionManager = class {
|
|
|
2160
2296
|
}
|
|
2161
2297
|
}
|
|
2162
2298
|
/**
|
|
2163
|
-
* Complete OAuth authorization
|
|
2299
|
+
* Complete OAuth authorization flow
|
|
2164
2300
|
*/
|
|
2165
2301
|
async finishAuth(params) {
|
|
2166
2302
|
const { sessionId, code } = params;
|
|
2167
|
-
this.sendEvent({
|
|
2168
|
-
level: "debug",
|
|
2169
|
-
message: `Completing OAuth for session ${sessionId}`,
|
|
2170
|
-
timestamp: Date.now(),
|
|
2171
|
-
metadata: { sessionId, identity: this.identity }
|
|
2172
|
-
});
|
|
2173
2303
|
const session = await storage.getSession(this.identity, sessionId);
|
|
2174
2304
|
if (!session) {
|
|
2175
2305
|
throw new Error("Session not found");
|
|
@@ -2177,8 +2307,8 @@ var SSEConnectionManager = class {
|
|
|
2177
2307
|
this.emitConnectionEvent({
|
|
2178
2308
|
type: "state_changed",
|
|
2179
2309
|
sessionId,
|
|
2180
|
-
serverId: session.serverId
|
|
2181
|
-
serverName: session.serverName
|
|
2310
|
+
serverId: session.serverId ?? "unknown",
|
|
2311
|
+
serverName: session.serverName ?? "Unknown",
|
|
2182
2312
|
state: "AUTHENTICATING",
|
|
2183
2313
|
previousState: "DISCONNECTED",
|
|
2184
2314
|
timestamp: Date.now()
|
|
@@ -2188,16 +2318,14 @@ var SSEConnectionManager = class {
|
|
|
2188
2318
|
identity: this.identity,
|
|
2189
2319
|
sessionId
|
|
2190
2320
|
});
|
|
2191
|
-
client.onConnectionEvent((event) =>
|
|
2192
|
-
this.emitConnectionEvent(event);
|
|
2193
|
-
});
|
|
2321
|
+
client.onConnectionEvent((event) => this.emitConnectionEvent(event));
|
|
2194
2322
|
await client.finishAuth(code);
|
|
2195
2323
|
this.clients.set(sessionId, client);
|
|
2196
2324
|
const tools = await client.listTools();
|
|
2197
2325
|
this.emitConnectionEvent({
|
|
2198
2326
|
type: "tools_discovered",
|
|
2199
2327
|
sessionId,
|
|
2200
|
-
serverId: session.serverId
|
|
2328
|
+
serverId: session.serverId ?? "unknown",
|
|
2201
2329
|
toolCount: tools.tools.length,
|
|
2202
2330
|
tools: tools.tools,
|
|
2203
2331
|
timestamp: Date.now()
|
|
@@ -2207,7 +2335,7 @@ var SSEConnectionManager = class {
|
|
|
2207
2335
|
this.emitConnectionEvent({
|
|
2208
2336
|
type: "error",
|
|
2209
2337
|
sessionId,
|
|
2210
|
-
serverId: session.serverId
|
|
2338
|
+
serverId: session.serverId ?? "unknown",
|
|
2211
2339
|
error: error instanceof Error ? error.message : "OAuth completion failed",
|
|
2212
2340
|
errorType: "auth",
|
|
2213
2341
|
timestamp: Date.now()
|
|
@@ -2247,7 +2375,7 @@ var SSEConnectionManager = class {
|
|
|
2247
2375
|
async readResource(params) {
|
|
2248
2376
|
const { sessionId, uri } = params;
|
|
2249
2377
|
const client = await this.getOrCreateClient(sessionId);
|
|
2250
|
-
return
|
|
2378
|
+
return client.readResource(uri);
|
|
2251
2379
|
}
|
|
2252
2380
|
/**
|
|
2253
2381
|
* Emit connection event
|
|
@@ -2277,19 +2405,17 @@ function createSSEHandler(options) {
|
|
|
2277
2405
|
"Connection": "keep-alive",
|
|
2278
2406
|
"Access-Control-Allow-Origin": "*"
|
|
2279
2407
|
});
|
|
2280
|
-
|
|
2408
|
+
writeSSEEvent(res, "connected", { timestamp: Date.now() });
|
|
2281
2409
|
const manager = new SSEConnectionManager(options, (event) => {
|
|
2282
2410
|
if ("id" in event) {
|
|
2283
|
-
|
|
2411
|
+
writeSSEEvent(res, "rpc-response", event);
|
|
2284
2412
|
} else if ("type" in event && "sessionId" in event) {
|
|
2285
|
-
|
|
2413
|
+
writeSSEEvent(res, "connection", event);
|
|
2286
2414
|
} else {
|
|
2287
|
-
|
|
2415
|
+
writeSSEEvent(res, "observability", event);
|
|
2288
2416
|
}
|
|
2289
2417
|
});
|
|
2290
|
-
req.on("close", () =>
|
|
2291
|
-
manager.dispose();
|
|
2292
|
-
});
|
|
2418
|
+
req.on("close", () => manager.dispose());
|
|
2293
2419
|
if (req.method === "POST") {
|
|
2294
2420
|
let body = "";
|
|
2295
2421
|
req.on("data", (chunk) => {
|
|
@@ -2299,14 +2425,13 @@ function createSSEHandler(options) {
|
|
|
2299
2425
|
try {
|
|
2300
2426
|
const request = JSON.parse(body);
|
|
2301
2427
|
await manager.handleRequest(request);
|
|
2302
|
-
} catch
|
|
2303
|
-
console.error("[SSE] Error handling request:", error);
|
|
2428
|
+
} catch {
|
|
2304
2429
|
}
|
|
2305
2430
|
});
|
|
2306
2431
|
}
|
|
2307
2432
|
};
|
|
2308
2433
|
}
|
|
2309
|
-
function
|
|
2434
|
+
function writeSSEEvent(res, event, data) {
|
|
2310
2435
|
res.write(`event: ${event}
|
|
2311
2436
|
`);
|
|
2312
2437
|
res.write(`data: ${JSON.stringify(data)}
|
|
@@ -2341,7 +2466,7 @@ function createNextMcpHandler(options = {}) {
|
|
|
2341
2466
|
const stream = new TransformStream();
|
|
2342
2467
|
const writer = stream.writable.getWriter();
|
|
2343
2468
|
const encoder = new TextEncoder();
|
|
2344
|
-
const
|
|
2469
|
+
const sendSSE = (event, data) => {
|
|
2345
2470
|
const message = `event: ${event}
|
|
2346
2471
|
data: ${JSON.stringify(data)}
|
|
2347
2472
|
|
|
@@ -2349,7 +2474,6 @@ data: ${JSON.stringify(data)}
|
|
|
2349
2474
|
writer.write(encoder.encode(message)).catch(() => {
|
|
2350
2475
|
});
|
|
2351
2476
|
};
|
|
2352
|
-
sendSSE2("connected", { timestamp: Date.now() });
|
|
2353
2477
|
const previousManager = managers.get(identity);
|
|
2354
2478
|
if (previousManager) {
|
|
2355
2479
|
previousManager.dispose();
|
|
@@ -2364,15 +2488,16 @@ data: ${JSON.stringify(data)}
|
|
|
2364
2488
|
},
|
|
2365
2489
|
(event) => {
|
|
2366
2490
|
if ("id" in event) {
|
|
2367
|
-
|
|
2491
|
+
sendSSE("rpc-response", event);
|
|
2368
2492
|
} else if ("type" in event && "sessionId" in event) {
|
|
2369
|
-
|
|
2493
|
+
sendSSE("connection", event);
|
|
2370
2494
|
} else {
|
|
2371
|
-
|
|
2495
|
+
sendSSE("observability", event);
|
|
2372
2496
|
}
|
|
2373
2497
|
}
|
|
2374
2498
|
);
|
|
2375
2499
|
managers.set(identity, manager);
|
|
2500
|
+
sendSSE("connected", { timestamp: Date.now() });
|
|
2376
2501
|
const abortController = new AbortController();
|
|
2377
2502
|
request.signal?.addEventListener("abort", () => {
|
|
2378
2503
|
manager.dispose();
|
|
@@ -2415,8 +2540,8 @@ data: ${JSON.stringify(data)}
|
|
|
2415
2540
|
{ status: 400 }
|
|
2416
2541
|
);
|
|
2417
2542
|
}
|
|
2418
|
-
await manager.handleRequest(body);
|
|
2419
|
-
return Response.json(
|
|
2543
|
+
const response = await manager.handleRequest(body);
|
|
2544
|
+
return Response.json(response);
|
|
2420
2545
|
} catch (error) {
|
|
2421
2546
|
return Response.json(
|
|
2422
2547
|
{
|