@mcp-ts/sdk 1.0.0 → 1.1.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 +25 -13
- package/dist/adapters/agui-adapter.d.mts +21 -44
- package/dist/adapters/agui-adapter.d.ts +21 -44
- package/dist/adapters/agui-adapter.js +93 -67
- package/dist/adapters/agui-adapter.js.map +1 -1
- package/dist/adapters/agui-adapter.mjs +93 -68
- package/dist/adapters/agui-adapter.mjs.map +1 -1
- package/dist/adapters/agui-middleware.d.mts +32 -134
- package/dist/adapters/agui-middleware.d.ts +32 -134
- package/dist/adapters/agui-middleware.js +314 -350
- package/dist/adapters/agui-middleware.js.map +1 -1
- package/dist/adapters/agui-middleware.mjs +314 -351
- 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 +184 -57
- package/dist/client/index.d.ts +184 -57
- 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 +40 -6
- package/dist/client/react.d.ts +40 -6
- package/dist/client/react.js +587 -142
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +586 -143
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +5 -5
- package/dist/client/vue.d.ts +5 -5
- package/dist/client/vue.js +545 -140
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +545 -141
- 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 +779 -248
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +775 -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 +44 -40
- package/dist/server/index.d.ts +44 -40
- package/dist/server/index.js +242 -116
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +238 -112
- 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.map +1 -1
- package/dist/shared/index.mjs.map +1 -1
- package/dist/{types-SbDlA2VX.d.mts → types-CLccx9wW.d.mts} +1 -1
- package/dist/{types-SbDlA2VX.d.ts → types-CLccx9wW.d.ts} +1 -1
- package/package.json +8 -1
- package/src/adapters/agui-adapter.ts +121 -107
- package/src/adapters/agui-middleware.ts +474 -512
- 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/index.ts +1 -0
- package/src/client/react/use-mcp-app.ts +73 -0
- package/src/client/react/useMcp.ts +18 -0
- package/src/server/handlers/nextjs-handler.ts +8 -7
- package/src/server/handlers/sse-handler.ts +131 -164
- 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/server/storage/types.ts +1 -1
- package/src/shared/events.ts +12 -0
- package/src/shared/types.ts +4 -2
package/dist/server/index.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/
|
|
|
4
4
|
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
5
5
|
import { UnauthorizedError as UnauthorizedError$1, discoverOAuthProtectedResourceMetadata, discoverAuthorizationServerMetadata, refreshAuthorization } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
6
6
|
import { ListToolsResultSchema, CallToolResultSchema, ListPromptsResultSchema, GetPromptResultSchema, ListResourcesResultSchema, ReadResourceResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
7
|
+
import * as fs2 from 'fs';
|
|
7
8
|
import { promises } from 'fs';
|
|
8
9
|
import * as path from 'path';
|
|
9
10
|
|
|
@@ -497,6 +498,148 @@ var FileStorageBackend = class {
|
|
|
497
498
|
async disconnect() {
|
|
498
499
|
}
|
|
499
500
|
};
|
|
501
|
+
var SqliteStorage = class {
|
|
502
|
+
constructor(options = {}) {
|
|
503
|
+
__publicField(this, "db", null);
|
|
504
|
+
__publicField(this, "table");
|
|
505
|
+
__publicField(this, "initialized", false);
|
|
506
|
+
__publicField(this, "dbPath");
|
|
507
|
+
this.dbPath = options.path || "./sessions.db";
|
|
508
|
+
this.table = options.table || "mcp_sessions";
|
|
509
|
+
}
|
|
510
|
+
async init() {
|
|
511
|
+
if (this.initialized) return;
|
|
512
|
+
try {
|
|
513
|
+
const DatabaseConstructor = (await import('better-sqlite3')).default;
|
|
514
|
+
const dir = path.dirname(this.dbPath);
|
|
515
|
+
if (!fs2.existsSync(dir)) {
|
|
516
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
517
|
+
}
|
|
518
|
+
this.db = new DatabaseConstructor(this.dbPath);
|
|
519
|
+
this.db.exec(`
|
|
520
|
+
CREATE TABLE IF NOT EXISTS ${this.table} (
|
|
521
|
+
sessionId TEXT PRIMARY KEY,
|
|
522
|
+
identity TEXT NOT NULL,
|
|
523
|
+
data TEXT NOT NULL,
|
|
524
|
+
expiresAt INTEGER
|
|
525
|
+
);
|
|
526
|
+
CREATE INDEX IF NOT EXISTS idx_${this.table}_identity ON ${this.table}(identity);
|
|
527
|
+
`);
|
|
528
|
+
this.initialized = true;
|
|
529
|
+
} catch (error) {
|
|
530
|
+
if (error.code === "MODULE_NOT_FOUND" || error.message?.includes("better-sqlite3")) {
|
|
531
|
+
throw new Error(
|
|
532
|
+
"better-sqlite3 is not installed. Please install it with: npm install better-sqlite3"
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
throw error;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
ensureInitialized() {
|
|
539
|
+
if (!this.initialized) {
|
|
540
|
+
throw new Error("SqliteStorage not initialized. Call init() first.");
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
generateSessionId() {
|
|
544
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
545
|
+
let result = "";
|
|
546
|
+
for (let i = 0; i < 32; i++) {
|
|
547
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
548
|
+
}
|
|
549
|
+
return result;
|
|
550
|
+
}
|
|
551
|
+
async createSession(session, ttl) {
|
|
552
|
+
this.ensureInitialized();
|
|
553
|
+
const { sessionId, identity } = session;
|
|
554
|
+
if (!sessionId || !identity) {
|
|
555
|
+
throw new Error("identity and sessionId required");
|
|
556
|
+
}
|
|
557
|
+
const expiresAt = ttl ? Date.now() + ttl * 1e3 : null;
|
|
558
|
+
try {
|
|
559
|
+
const stmt = this.db.prepare(
|
|
560
|
+
`INSERT INTO ${this.table} (sessionId, identity, data, expiresAt) VALUES (?, ?, ?, ?)`
|
|
561
|
+
);
|
|
562
|
+
stmt.run(sessionId, identity, JSON.stringify(session), expiresAt);
|
|
563
|
+
} catch (error) {
|
|
564
|
+
if (error.code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
|
|
565
|
+
throw new Error(`Session ${sessionId} already exists`);
|
|
566
|
+
}
|
|
567
|
+
throw error;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
async updateSession(identity, sessionId, data, ttl) {
|
|
571
|
+
this.ensureInitialized();
|
|
572
|
+
if (!sessionId || !identity) {
|
|
573
|
+
throw new Error("identity and sessionId required");
|
|
574
|
+
}
|
|
575
|
+
const currentSession = await this.getSession(identity, sessionId);
|
|
576
|
+
if (!currentSession) {
|
|
577
|
+
throw new Error(`Session ${sessionId} not found for identity ${identity}`);
|
|
578
|
+
}
|
|
579
|
+
const updatedSession = { ...currentSession, ...data };
|
|
580
|
+
const expiresAt = ttl ? Date.now() + ttl * 1e3 : null;
|
|
581
|
+
const stmt = this.db.prepare(
|
|
582
|
+
`UPDATE ${this.table} SET data = ?, expiresAt = ? WHERE sessionId = ? AND identity = ?`
|
|
583
|
+
);
|
|
584
|
+
stmt.run(JSON.stringify(updatedSession), expiresAt, sessionId, identity);
|
|
585
|
+
}
|
|
586
|
+
async getSession(identity, sessionId) {
|
|
587
|
+
this.ensureInitialized();
|
|
588
|
+
const stmt = this.db.prepare(
|
|
589
|
+
`SELECT data FROM ${this.table} WHERE sessionId = ? AND identity = ?`
|
|
590
|
+
);
|
|
591
|
+
const row = stmt.get(sessionId, identity);
|
|
592
|
+
if (!row) return null;
|
|
593
|
+
return JSON.parse(row.data);
|
|
594
|
+
}
|
|
595
|
+
async getIdentitySessionsData(identity) {
|
|
596
|
+
this.ensureInitialized();
|
|
597
|
+
const stmt = this.db.prepare(
|
|
598
|
+
`SELECT data FROM ${this.table} WHERE identity = ?`
|
|
599
|
+
);
|
|
600
|
+
const rows = stmt.all(identity);
|
|
601
|
+
return rows.map((row) => JSON.parse(row.data));
|
|
602
|
+
}
|
|
603
|
+
async getIdentityMcpSessions(identity) {
|
|
604
|
+
this.ensureInitialized();
|
|
605
|
+
const stmt = this.db.prepare(
|
|
606
|
+
`SELECT sessionId FROM ${this.table} WHERE identity = ?`
|
|
607
|
+
);
|
|
608
|
+
const rows = stmt.all(identity);
|
|
609
|
+
return rows.map((row) => row.sessionId);
|
|
610
|
+
}
|
|
611
|
+
async removeSession(identity, sessionId) {
|
|
612
|
+
this.ensureInitialized();
|
|
613
|
+
const stmt = this.db.prepare(
|
|
614
|
+
`DELETE FROM ${this.table} WHERE sessionId = ? AND identity = ?`
|
|
615
|
+
);
|
|
616
|
+
stmt.run(sessionId, identity);
|
|
617
|
+
}
|
|
618
|
+
async getAllSessionIds() {
|
|
619
|
+
this.ensureInitialized();
|
|
620
|
+
const stmt = this.db.prepare(`SELECT sessionId FROM ${this.table}`);
|
|
621
|
+
const rows = stmt.all();
|
|
622
|
+
return rows.map((row) => row.sessionId);
|
|
623
|
+
}
|
|
624
|
+
async clearAll() {
|
|
625
|
+
this.ensureInitialized();
|
|
626
|
+
const stmt = this.db.prepare(`DELETE FROM ${this.table}`);
|
|
627
|
+
stmt.run();
|
|
628
|
+
}
|
|
629
|
+
async cleanupExpiredSessions() {
|
|
630
|
+
this.ensureInitialized();
|
|
631
|
+
const now = Date.now();
|
|
632
|
+
const stmt = this.db.prepare(
|
|
633
|
+
`DELETE FROM ${this.table} WHERE expiresAt IS NOT NULL AND expiresAt < ?`
|
|
634
|
+
);
|
|
635
|
+
stmt.run(now);
|
|
636
|
+
}
|
|
637
|
+
async disconnect() {
|
|
638
|
+
if (this.db) {
|
|
639
|
+
this.db.close();
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
};
|
|
500
643
|
|
|
501
644
|
// src/server/storage/index.ts
|
|
502
645
|
var storageInstance = null;
|
|
@@ -528,6 +671,13 @@ async function createStorage() {
|
|
|
528
671
|
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
529
672
|
return store;
|
|
530
673
|
}
|
|
674
|
+
if (type === "sqlite") {
|
|
675
|
+
const dbPath = process.env.MCP_TS_STORAGE_SQLITE_PATH;
|
|
676
|
+
console.log(`[Storage] Using SQLite storage (${dbPath || "default"}) (Explicit)`);
|
|
677
|
+
const store = new SqliteStorage({ path: dbPath });
|
|
678
|
+
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
679
|
+
return store;
|
|
680
|
+
}
|
|
531
681
|
if (type === "memory") {
|
|
532
682
|
console.log("[Storage] Using In-Memory storage (Explicit)");
|
|
533
683
|
return new MemoryStorageBackend();
|
|
@@ -550,6 +700,12 @@ async function createStorage() {
|
|
|
550
700
|
store.init().catch((err) => console.error("[Storage] Failed to initialize file storage:", err));
|
|
551
701
|
return store;
|
|
552
702
|
}
|
|
703
|
+
if (process.env.MCP_TS_STORAGE_SQLITE_PATH) {
|
|
704
|
+
console.log(`[Storage] Auto-detected MCP_TS_STORAGE_SQLITE_PATH. Using SQLite storage (${process.env.MCP_TS_STORAGE_SQLITE_PATH}).`);
|
|
705
|
+
const store = new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH });
|
|
706
|
+
store.init().catch((err) => console.error("[Storage] Failed to initialize SQLite storage:", err));
|
|
707
|
+
return store;
|
|
708
|
+
}
|
|
553
709
|
console.log("[Storage] No storage configured. Using In-Memory storage (Default).");
|
|
554
710
|
return new MemoryStorageBackend();
|
|
555
711
|
}
|
|
@@ -1057,7 +1213,15 @@ var MCPClient = class _MCPClient {
|
|
|
1057
1213
|
name: "mcp-ts-oauth-client",
|
|
1058
1214
|
version: "2.0"
|
|
1059
1215
|
},
|
|
1060
|
-
{
|
|
1216
|
+
{
|
|
1217
|
+
capabilities: {
|
|
1218
|
+
extensions: {
|
|
1219
|
+
"io.modelcontextprotocol/ui": {
|
|
1220
|
+
mimeTypes: ["text/html+mcp"]
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1061
1225
|
);
|
|
1062
1226
|
}
|
|
1063
1227
|
const existingSession = await storage.getSession(this.identity, this.sessionId);
|
|
@@ -1239,7 +1403,15 @@ var MCPClient = class _MCPClient {
|
|
|
1239
1403
|
name: "mcp-ts-oauth-client",
|
|
1240
1404
|
version: "2.0"
|
|
1241
1405
|
},
|
|
1242
|
-
{
|
|
1406
|
+
{
|
|
1407
|
+
capabilities: {
|
|
1408
|
+
extensions: {
|
|
1409
|
+
"io.modelcontextprotocol/ui": {
|
|
1410
|
+
mimeTypes: ["text/html+mcp"]
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1243
1415
|
);
|
|
1244
1416
|
this.emitStateChange("CONNECTING");
|
|
1245
1417
|
await this.client.connect(this.transport);
|
|
@@ -1795,6 +1967,7 @@ var MultiSessionClient = class {
|
|
|
1795
1967
|
};
|
|
1796
1968
|
|
|
1797
1969
|
// src/server/handlers/sse-handler.ts
|
|
1970
|
+
var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
|
|
1798
1971
|
var SSEConnectionManager = class {
|
|
1799
1972
|
constructor(options, sendEvent) {
|
|
1800
1973
|
this.options = options;
|
|
@@ -1824,7 +1997,7 @@ var SSEConnectionManager = class {
|
|
|
1824
1997
|
* Start heartbeat to keep connection alive
|
|
1825
1998
|
*/
|
|
1826
1999
|
startHeartbeat() {
|
|
1827
|
-
const interval = this.options.heartbeatInterval
|
|
2000
|
+
const interval = this.options.heartbeatInterval ?? DEFAULT_HEARTBEAT_INTERVAL;
|
|
1828
2001
|
this.heartbeatTimer = setInterval(() => {
|
|
1829
2002
|
if (this.isActive) {
|
|
1830
2003
|
this.sendEvent({
|
|
@@ -1837,6 +2010,7 @@ var SSEConnectionManager = class {
|
|
|
1837
2010
|
}
|
|
1838
2011
|
/**
|
|
1839
2012
|
* Handle incoming RPC requests
|
|
2013
|
+
* Returns the RPC response directly for immediate HTTP response (bypassing SSE latency)
|
|
1840
2014
|
*/
|
|
1841
2015
|
async handleRequest(request) {
|
|
1842
2016
|
try {
|
|
@@ -1878,39 +2052,29 @@ var SSEConnectionManager = class {
|
|
|
1878
2052
|
default:
|
|
1879
2053
|
throw new Error(`Unknown method: ${request.method}`);
|
|
1880
2054
|
}
|
|
1881
|
-
|
|
2055
|
+
const response = {
|
|
1882
2056
|
id: request.id,
|
|
1883
2057
|
result
|
|
1884
|
-
}
|
|
2058
|
+
};
|
|
2059
|
+
this.sendEvent(response);
|
|
2060
|
+
return response;
|
|
1885
2061
|
} catch (error) {
|
|
1886
|
-
|
|
2062
|
+
const errorResponse = {
|
|
1887
2063
|
id: request.id,
|
|
1888
2064
|
error: {
|
|
1889
2065
|
code: RpcErrorCodes.EXECUTION_ERROR,
|
|
1890
2066
|
message: error instanceof Error ? error.message : "Unknown error"
|
|
1891
2067
|
}
|
|
1892
|
-
}
|
|
2068
|
+
};
|
|
2069
|
+
this.sendEvent(errorResponse);
|
|
2070
|
+
return errorResponse;
|
|
1893
2071
|
}
|
|
1894
2072
|
}
|
|
1895
2073
|
/**
|
|
1896
|
-
* Get all
|
|
2074
|
+
* Get all sessions for the current identity
|
|
1897
2075
|
*/
|
|
1898
2076
|
async getSessions() {
|
|
1899
2077
|
const sessions = await storage.getIdentitySessionsData(this.identity);
|
|
1900
|
-
this.sendEvent({
|
|
1901
|
-
level: "debug",
|
|
1902
|
-
message: `Retrieved ${sessions.length} sessions for identity ${this.identity}`,
|
|
1903
|
-
timestamp: Date.now(),
|
|
1904
|
-
metadata: {
|
|
1905
|
-
identity: this.identity,
|
|
1906
|
-
sessionCount: sessions.length,
|
|
1907
|
-
sessions: sessions.map((s) => ({
|
|
1908
|
-
sessionId: s.sessionId,
|
|
1909
|
-
serverId: s.serverId,
|
|
1910
|
-
serverName: s.serverName
|
|
1911
|
-
}))
|
|
1912
|
-
}
|
|
1913
|
-
});
|
|
1914
2078
|
return {
|
|
1915
2079
|
sessions: sessions.map((s) => ({
|
|
1916
2080
|
sessionId: s.sessionId,
|
|
@@ -1925,7 +2089,8 @@ var SSEConnectionManager = class {
|
|
|
1925
2089
|
* Connect to an MCP server
|
|
1926
2090
|
*/
|
|
1927
2091
|
async connect(params) {
|
|
1928
|
-
const {
|
|
2092
|
+
const { serverName, serverUrl, callbackUrl, transportType } = params;
|
|
2093
|
+
const serverId = params.serverId && params.serverId.length <= 12 ? params.serverId : await storage.generateSessionId();
|
|
1929
2094
|
const existingSessions = await storage.getIdentitySessionsData(this.identity);
|
|
1930
2095
|
const duplicate = existingSessions.find(
|
|
1931
2096
|
(s) => s.serverId === serverId || s.serverUrl === serverUrl
|
|
@@ -1974,10 +2139,6 @@ var SSEConnectionManager = class {
|
|
|
1974
2139
|
});
|
|
1975
2140
|
await client.connect();
|
|
1976
2141
|
const tools = await client.listTools();
|
|
1977
|
-
const sessionAfterConnect = await storage.getSession(this.identity, sessionId);
|
|
1978
|
-
console.log(`[SSE Handler] After connect() - Session ${sessionId}:`, {
|
|
1979
|
-
serverId: sessionAfterConnect?.serverId
|
|
1980
|
-
});
|
|
1981
2142
|
this.emitConnectionEvent({
|
|
1982
2143
|
type: "tools_discovered",
|
|
1983
2144
|
sessionId,
|
|
@@ -2019,24 +2180,21 @@ var SSEConnectionManager = class {
|
|
|
2019
2180
|
return { success: true };
|
|
2020
2181
|
}
|
|
2021
2182
|
/**
|
|
2022
|
-
*
|
|
2183
|
+
* Get an existing client or create and connect a new one for the session.
|
|
2023
2184
|
*/
|
|
2024
2185
|
async getOrCreateClient(sessionId) {
|
|
2025
|
-
|
|
2026
|
-
if (
|
|
2027
|
-
|
|
2028
|
-
identity: this.identity,
|
|
2029
|
-
sessionId
|
|
2030
|
-
});
|
|
2031
|
-
client.onConnectionEvent((event) => {
|
|
2032
|
-
this.emitConnectionEvent(event);
|
|
2033
|
-
});
|
|
2034
|
-
client.onObservabilityEvent((event) => {
|
|
2035
|
-
this.sendEvent(event);
|
|
2036
|
-
});
|
|
2037
|
-
await client.connect();
|
|
2038
|
-
this.clients.set(sessionId, client);
|
|
2186
|
+
const existing = this.clients.get(sessionId);
|
|
2187
|
+
if (existing) {
|
|
2188
|
+
return existing;
|
|
2039
2189
|
}
|
|
2190
|
+
const client = new MCPClient({
|
|
2191
|
+
identity: this.identity,
|
|
2192
|
+
sessionId
|
|
2193
|
+
});
|
|
2194
|
+
client.onConnectionEvent((event) => this.emitConnectionEvent(event));
|
|
2195
|
+
client.onObservabilityEvent((event) => this.sendEvent(event));
|
|
2196
|
+
await client.connect();
|
|
2197
|
+
this.clients.set(sessionId, client);
|
|
2040
2198
|
return client;
|
|
2041
2199
|
}
|
|
2042
2200
|
/**
|
|
@@ -2049,51 +2207,35 @@ var SSEConnectionManager = class {
|
|
|
2049
2207
|
return { tools: result.tools };
|
|
2050
2208
|
}
|
|
2051
2209
|
/**
|
|
2052
|
-
* Call a tool
|
|
2210
|
+
* Call a tool on the MCP server
|
|
2053
2211
|
*/
|
|
2054
2212
|
async callTool(params) {
|
|
2055
2213
|
const { sessionId, toolName, toolArgs } = params;
|
|
2056
2214
|
const client = await this.getOrCreateClient(sessionId);
|
|
2057
|
-
|
|
2215
|
+
const result = await client.callTool(toolName, toolArgs);
|
|
2216
|
+
const meta = result._meta || {};
|
|
2217
|
+
return {
|
|
2218
|
+
...result,
|
|
2219
|
+
_meta: {
|
|
2220
|
+
...meta,
|
|
2221
|
+
sessionId
|
|
2222
|
+
}
|
|
2223
|
+
};
|
|
2058
2224
|
}
|
|
2059
2225
|
/**
|
|
2060
|
-
*
|
|
2226
|
+
* Restore and validate an existing session
|
|
2061
2227
|
*/
|
|
2062
2228
|
async restoreSession(params) {
|
|
2063
2229
|
const { sessionId } = params;
|
|
2064
|
-
this.sendEvent({
|
|
2065
|
-
level: "debug",
|
|
2066
|
-
message: `Starting session refresh for ${sessionId}`,
|
|
2067
|
-
timestamp: Date.now(),
|
|
2068
|
-
metadata: { sessionId, identity: this.identity }
|
|
2069
|
-
});
|
|
2070
2230
|
const session = await storage.getSession(this.identity, sessionId);
|
|
2071
2231
|
if (!session) {
|
|
2072
|
-
this.sendEvent({
|
|
2073
|
-
level: "error",
|
|
2074
|
-
message: `Session not found: ${sessionId}`,
|
|
2075
|
-
timestamp: Date.now(),
|
|
2076
|
-
metadata: { sessionId, identity: this.identity }
|
|
2077
|
-
});
|
|
2078
2232
|
throw new Error("Session not found");
|
|
2079
2233
|
}
|
|
2080
|
-
this.sendEvent({
|
|
2081
|
-
level: "debug",
|
|
2082
|
-
message: `Session found in Redis`,
|
|
2083
|
-
timestamp: Date.now(),
|
|
2084
|
-
metadata: {
|
|
2085
|
-
sessionId,
|
|
2086
|
-
serverId: session.serverId,
|
|
2087
|
-
serverName: session.serverName,
|
|
2088
|
-
serverUrl: session.serverUrl,
|
|
2089
|
-
transportType: session.transportType
|
|
2090
|
-
}
|
|
2091
|
-
});
|
|
2092
2234
|
this.emitConnectionEvent({
|
|
2093
2235
|
type: "state_changed",
|
|
2094
2236
|
sessionId,
|
|
2095
|
-
serverId: session.serverId
|
|
2096
|
-
serverName: session.serverName
|
|
2237
|
+
serverId: session.serverId ?? "unknown",
|
|
2238
|
+
serverName: session.serverName ?? "Unknown",
|
|
2097
2239
|
state: "VALIDATING",
|
|
2098
2240
|
previousState: "DISCONNECTED",
|
|
2099
2241
|
timestamp: Date.now()
|
|
@@ -2104,21 +2246,16 @@ var SSEConnectionManager = class {
|
|
|
2104
2246
|
identity: this.identity,
|
|
2105
2247
|
sessionId,
|
|
2106
2248
|
...clientMetadata
|
|
2107
|
-
// Include metadata for consistency
|
|
2108
|
-
});
|
|
2109
|
-
client.onConnectionEvent((event) => {
|
|
2110
|
-
this.emitConnectionEvent(event);
|
|
2111
|
-
});
|
|
2112
|
-
client.onObservabilityEvent((event) => {
|
|
2113
|
-
this.sendEvent(event);
|
|
2114
2249
|
});
|
|
2250
|
+
client.onConnectionEvent((event) => this.emitConnectionEvent(event));
|
|
2251
|
+
client.onObservabilityEvent((event) => this.sendEvent(event));
|
|
2115
2252
|
await client.connect();
|
|
2116
2253
|
this.clients.set(sessionId, client);
|
|
2117
2254
|
const tools = await client.listTools();
|
|
2118
2255
|
this.emitConnectionEvent({
|
|
2119
2256
|
type: "tools_discovered",
|
|
2120
2257
|
sessionId,
|
|
2121
|
-
serverId: session.serverId
|
|
2258
|
+
serverId: session.serverId ?? "unknown",
|
|
2122
2259
|
toolCount: tools.tools.length,
|
|
2123
2260
|
tools: tools.tools,
|
|
2124
2261
|
timestamp: Date.now()
|
|
@@ -2128,7 +2265,7 @@ var SSEConnectionManager = class {
|
|
|
2128
2265
|
this.emitConnectionEvent({
|
|
2129
2266
|
type: "error",
|
|
2130
2267
|
sessionId,
|
|
2131
|
-
serverId: session.serverId
|
|
2268
|
+
serverId: session.serverId ?? "unknown",
|
|
2132
2269
|
error: error instanceof Error ? error.message : "Validation failed",
|
|
2133
2270
|
errorType: "validation",
|
|
2134
2271
|
timestamp: Date.now()
|
|
@@ -2137,16 +2274,10 @@ var SSEConnectionManager = class {
|
|
|
2137
2274
|
}
|
|
2138
2275
|
}
|
|
2139
2276
|
/**
|
|
2140
|
-
* Complete OAuth authorization
|
|
2277
|
+
* Complete OAuth authorization flow
|
|
2141
2278
|
*/
|
|
2142
2279
|
async finishAuth(params) {
|
|
2143
2280
|
const { sessionId, code } = params;
|
|
2144
|
-
this.sendEvent({
|
|
2145
|
-
level: "debug",
|
|
2146
|
-
message: `Completing OAuth for session ${sessionId}`,
|
|
2147
|
-
timestamp: Date.now(),
|
|
2148
|
-
metadata: { sessionId, identity: this.identity }
|
|
2149
|
-
});
|
|
2150
2281
|
const session = await storage.getSession(this.identity, sessionId);
|
|
2151
2282
|
if (!session) {
|
|
2152
2283
|
throw new Error("Session not found");
|
|
@@ -2154,8 +2285,8 @@ var SSEConnectionManager = class {
|
|
|
2154
2285
|
this.emitConnectionEvent({
|
|
2155
2286
|
type: "state_changed",
|
|
2156
2287
|
sessionId,
|
|
2157
|
-
serverId: session.serverId
|
|
2158
|
-
serverName: session.serverName
|
|
2288
|
+
serverId: session.serverId ?? "unknown",
|
|
2289
|
+
serverName: session.serverName ?? "Unknown",
|
|
2159
2290
|
state: "AUTHENTICATING",
|
|
2160
2291
|
previousState: "DISCONNECTED",
|
|
2161
2292
|
timestamp: Date.now()
|
|
@@ -2165,16 +2296,14 @@ var SSEConnectionManager = class {
|
|
|
2165
2296
|
identity: this.identity,
|
|
2166
2297
|
sessionId
|
|
2167
2298
|
});
|
|
2168
|
-
client.onConnectionEvent((event) =>
|
|
2169
|
-
this.emitConnectionEvent(event);
|
|
2170
|
-
});
|
|
2299
|
+
client.onConnectionEvent((event) => this.emitConnectionEvent(event));
|
|
2171
2300
|
await client.finishAuth(code);
|
|
2172
2301
|
this.clients.set(sessionId, client);
|
|
2173
2302
|
const tools = await client.listTools();
|
|
2174
2303
|
this.emitConnectionEvent({
|
|
2175
2304
|
type: "tools_discovered",
|
|
2176
2305
|
sessionId,
|
|
2177
|
-
serverId: session.serverId
|
|
2306
|
+
serverId: session.serverId ?? "unknown",
|
|
2178
2307
|
toolCount: tools.tools.length,
|
|
2179
2308
|
tools: tools.tools,
|
|
2180
2309
|
timestamp: Date.now()
|
|
@@ -2184,7 +2313,7 @@ var SSEConnectionManager = class {
|
|
|
2184
2313
|
this.emitConnectionEvent({
|
|
2185
2314
|
type: "error",
|
|
2186
2315
|
sessionId,
|
|
2187
|
-
serverId: session.serverId
|
|
2316
|
+
serverId: session.serverId ?? "unknown",
|
|
2188
2317
|
error: error instanceof Error ? error.message : "OAuth completion failed",
|
|
2189
2318
|
errorType: "auth",
|
|
2190
2319
|
timestamp: Date.now()
|
|
@@ -2224,7 +2353,7 @@ var SSEConnectionManager = class {
|
|
|
2224
2353
|
async readResource(params) {
|
|
2225
2354
|
const { sessionId, uri } = params;
|
|
2226
2355
|
const client = await this.getOrCreateClient(sessionId);
|
|
2227
|
-
return
|
|
2356
|
+
return client.readResource(uri);
|
|
2228
2357
|
}
|
|
2229
2358
|
/**
|
|
2230
2359
|
* Emit connection event
|
|
@@ -2254,19 +2383,17 @@ function createSSEHandler(options) {
|
|
|
2254
2383
|
"Connection": "keep-alive",
|
|
2255
2384
|
"Access-Control-Allow-Origin": "*"
|
|
2256
2385
|
});
|
|
2257
|
-
|
|
2386
|
+
writeSSEEvent(res, "connected", { timestamp: Date.now() });
|
|
2258
2387
|
const manager = new SSEConnectionManager(options, (event) => {
|
|
2259
2388
|
if ("id" in event) {
|
|
2260
|
-
|
|
2389
|
+
writeSSEEvent(res, "rpc-response", event);
|
|
2261
2390
|
} else if ("type" in event && "sessionId" in event) {
|
|
2262
|
-
|
|
2391
|
+
writeSSEEvent(res, "connection", event);
|
|
2263
2392
|
} else {
|
|
2264
|
-
|
|
2393
|
+
writeSSEEvent(res, "observability", event);
|
|
2265
2394
|
}
|
|
2266
2395
|
});
|
|
2267
|
-
req.on("close", () =>
|
|
2268
|
-
manager.dispose();
|
|
2269
|
-
});
|
|
2396
|
+
req.on("close", () => manager.dispose());
|
|
2270
2397
|
if (req.method === "POST") {
|
|
2271
2398
|
let body = "";
|
|
2272
2399
|
req.on("data", (chunk) => {
|
|
@@ -2276,14 +2403,13 @@ function createSSEHandler(options) {
|
|
|
2276
2403
|
try {
|
|
2277
2404
|
const request = JSON.parse(body);
|
|
2278
2405
|
await manager.handleRequest(request);
|
|
2279
|
-
} catch
|
|
2280
|
-
console.error("[SSE] Error handling request:", error);
|
|
2406
|
+
} catch {
|
|
2281
2407
|
}
|
|
2282
2408
|
});
|
|
2283
2409
|
}
|
|
2284
2410
|
};
|
|
2285
2411
|
}
|
|
2286
|
-
function
|
|
2412
|
+
function writeSSEEvent(res, event, data) {
|
|
2287
2413
|
res.write(`event: ${event}
|
|
2288
2414
|
`);
|
|
2289
2415
|
res.write(`data: ${JSON.stringify(data)}
|
|
@@ -2318,7 +2444,7 @@ function createNextMcpHandler(options = {}) {
|
|
|
2318
2444
|
const stream = new TransformStream();
|
|
2319
2445
|
const writer = stream.writable.getWriter();
|
|
2320
2446
|
const encoder = new TextEncoder();
|
|
2321
|
-
const
|
|
2447
|
+
const sendSSE = (event, data) => {
|
|
2322
2448
|
const message = `event: ${event}
|
|
2323
2449
|
data: ${JSON.stringify(data)}
|
|
2324
2450
|
|
|
@@ -2326,7 +2452,6 @@ data: ${JSON.stringify(data)}
|
|
|
2326
2452
|
writer.write(encoder.encode(message)).catch(() => {
|
|
2327
2453
|
});
|
|
2328
2454
|
};
|
|
2329
|
-
sendSSE2("connected", { timestamp: Date.now() });
|
|
2330
2455
|
const previousManager = managers.get(identity);
|
|
2331
2456
|
if (previousManager) {
|
|
2332
2457
|
previousManager.dispose();
|
|
@@ -2341,15 +2466,16 @@ data: ${JSON.stringify(data)}
|
|
|
2341
2466
|
},
|
|
2342
2467
|
(event) => {
|
|
2343
2468
|
if ("id" in event) {
|
|
2344
|
-
|
|
2469
|
+
sendSSE("rpc-response", event);
|
|
2345
2470
|
} else if ("type" in event && "sessionId" in event) {
|
|
2346
|
-
|
|
2471
|
+
sendSSE("connection", event);
|
|
2347
2472
|
} else {
|
|
2348
|
-
|
|
2473
|
+
sendSSE("observability", event);
|
|
2349
2474
|
}
|
|
2350
2475
|
}
|
|
2351
2476
|
);
|
|
2352
2477
|
managers.set(identity, manager);
|
|
2478
|
+
sendSSE("connected", { timestamp: Date.now() });
|
|
2353
2479
|
const abortController = new AbortController();
|
|
2354
2480
|
request.signal?.addEventListener("abort", () => {
|
|
2355
2481
|
manager.dispose();
|
|
@@ -2392,8 +2518,8 @@ data: ${JSON.stringify(data)}
|
|
|
2392
2518
|
{ status: 400 }
|
|
2393
2519
|
);
|
|
2394
2520
|
}
|
|
2395
|
-
await manager.handleRequest(body);
|
|
2396
|
-
return Response.json(
|
|
2521
|
+
const response = await manager.handleRequest(body);
|
|
2522
|
+
return Response.json(response);
|
|
2397
2523
|
} catch (error) {
|
|
2398
2524
|
return Response.json(
|
|
2399
2525
|
{
|