@mcp-ts/sdk 2.4.4 → 2.5.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/dist/adapters/agui-adapter.d.mts +3 -3
- package/dist/adapters/agui-adapter.d.ts +3 -3
- package/dist/adapters/agui-adapter.js.map +1 -1
- package/dist/adapters/agui-adapter.mjs.map +1 -1
- package/dist/adapters/agui-middleware.d.mts +3 -3
- package/dist/adapters/agui-middleware.d.ts +3 -3
- package/dist/adapters/agui-middleware.js.map +1 -1
- package/dist/adapters/agui-middleware.mjs.map +1 -1
- package/dist/adapters/ai-adapter.d.mts +3 -3
- package/dist/adapters/ai-adapter.d.ts +3 -3
- package/dist/adapters/ai-adapter.js.map +1 -1
- package/dist/adapters/ai-adapter.mjs.map +1 -1
- package/dist/adapters/langchain-adapter.d.mts +3 -3
- package/dist/adapters/langchain-adapter.d.ts +3 -3
- package/dist/adapters/langchain-adapter.js +2 -2
- package/dist/adapters/langchain-adapter.js.map +1 -1
- package/dist/adapters/langchain-adapter.mjs +2 -2
- package/dist/adapters/langchain-adapter.mjs.map +1 -1
- package/dist/adapters/mastra-adapter.d.mts +2 -2
- package/dist/adapters/mastra-adapter.d.ts +2 -2
- package/dist/adapters/mastra-adapter.js +2 -2
- package/dist/adapters/mastra-adapter.js.map +1 -1
- package/dist/adapters/mastra-adapter.mjs +2 -2
- package/dist/adapters/mastra-adapter.mjs.map +1 -1
- package/dist/client/index.d.mts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +6 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +6 -0
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +15 -4
- package/dist/client/react.d.ts +15 -4
- package/dist/client/react.js +36 -6
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +36 -6
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +4 -4
- package/dist/client/vue.d.ts +4 -4
- package/dist/client/vue.js +6 -0
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +6 -0
- package/dist/client/vue.mjs.map +1 -1
- package/dist/{index-CkM3p0eE.d.mts → index-CTURVnom.d.mts} +5 -1
- package/dist/{index-CZk2gu2E.d.ts → index-sVcqrhf7.d.ts} +5 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +374 -130
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +370 -130
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-ChQrBZhF.d.mts → multi-session-client-CSPSHHla.d.ts} +14 -2
- package/dist/{multi-session-client-QOOPdEVp.d.ts → multi-session-client-DMZGVABI.d.mts} +14 -2
- package/dist/server/index.d.mts +13 -4
- package/dist/server/index.d.ts +13 -4
- package/dist/server/index.js +368 -130
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +364 -130
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +4 -4
- package/dist/shared/index.d.ts +4 -4
- package/dist/shared/index.js +2 -6
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +2 -6
- package/dist/shared/index.mjs.map +1 -1
- package/dist/{tool-router-DQ-HrD7W.d.ts → tool-router-CS9l0w4a.d.ts} +1 -1
- package/dist/{tool-router-CGs3IDOJ.d.mts → tool-router-CVLBaCwH.d.mts} +1 -1
- package/dist/{types-Bf-7GOLW.d.mts → types-DK_NGWd4.d.mts} +37 -3
- package/dist/{types-Bf-7GOLW.d.ts → types-DK_NGWd4.d.ts} +37 -3
- package/migrations/neon/20260513010000_install_mcp_sessions.sql +1 -0
- package/migrations/supabase/20260330195700_install_mcp_sessions.sql +1 -0
- package/package.json +1 -1
- package/src/adapters/agui-adapter.ts +6 -2
- package/src/adapters/ai-adapter.ts +3 -1
- package/src/adapters/langchain-adapter.ts +5 -3
- package/src/adapters/mastra-adapter.ts +5 -3
- package/src/client/core/sse-client.ts +17 -0
- package/src/client/react/use-mcp.ts +63 -0
- package/src/server/handlers/sse-handler.ts +117 -10
- package/src/server/mcp/multi-session-client.ts +7 -3
- package/src/server/mcp/oauth-client.ts +78 -55
- package/src/server/mcp/tool-policy-gateway.ts +90 -0
- package/src/server/storage/index.ts +1 -1
- package/src/server/storage/neon-backend.ts +65 -60
- package/src/server/storage/session-lifecycle.ts +5 -0
- package/src/server/storage/supabase-backend.ts +27 -15
- package/src/server/storage/tool-policy.ts +89 -0
- package/src/server/storage/types.ts +11 -0
- package/src/shared/types.ts +48 -1
- package/src/shared/utils.ts +16 -14
package/dist/server/index.mjs
CHANGED
|
@@ -112,13 +112,9 @@ var init_redis = __esm({
|
|
|
112
112
|
}
|
|
113
113
|
});
|
|
114
114
|
var OAUTH_STATE_SEPARATOR = ".";
|
|
115
|
-
var
|
|
116
|
-
"
|
|
117
|
-
|
|
118
|
-
);
|
|
119
|
-
var rest = customAlphabet(
|
|
120
|
-
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
|
121
|
-
11
|
|
115
|
+
var serverIdAlphabet = customAlphabet(
|
|
116
|
+
"abcdefghijklmnopqrstuvwxyz0123456789",
|
|
117
|
+
12
|
|
122
118
|
);
|
|
123
119
|
function sanitizeServerLabel(name) {
|
|
124
120
|
let sanitized = name.replace(/[^a-zA-Z0-9-_]/g, "_").replace(/_{2,}/g, "_").toLowerCase();
|
|
@@ -128,7 +124,10 @@ function sanitizeServerLabel(name) {
|
|
|
128
124
|
return sanitized;
|
|
129
125
|
}
|
|
130
126
|
function generateSessionId() {
|
|
131
|
-
return
|
|
127
|
+
return "sess_" + nanoid(21);
|
|
128
|
+
}
|
|
129
|
+
function generateServerId() {
|
|
130
|
+
return serverIdAlphabet();
|
|
132
131
|
}
|
|
133
132
|
function formatOAuthState(nonce, sessionId) {
|
|
134
133
|
return `${nonce}${OAUTH_STATE_SEPARATOR}${sessionId}`;
|
|
@@ -159,6 +158,59 @@ var SOFTWARE_VERSION = "2.3.4";
|
|
|
159
158
|
var MCP_CLIENT_NAME = "mcp-ts-oauth-client";
|
|
160
159
|
var MCP_CLIENT_VERSION = "2.0";
|
|
161
160
|
|
|
161
|
+
// src/server/storage/tool-policy.ts
|
|
162
|
+
function createToolId(serverId, toolName) {
|
|
163
|
+
return `${serverId}::${toolName}`;
|
|
164
|
+
}
|
|
165
|
+
function normalizeToolIds(input) {
|
|
166
|
+
if (!Array.isArray(input)) return [];
|
|
167
|
+
return Array.from(new Set(
|
|
168
|
+
input.filter((id) => typeof id === "string").map((id) => id.trim()).filter(Boolean)
|
|
169
|
+
));
|
|
170
|
+
}
|
|
171
|
+
function normalizeToolPolicy(input, now = Date.now()) {
|
|
172
|
+
if (!input || typeof input !== "object") {
|
|
173
|
+
return void 0;
|
|
174
|
+
}
|
|
175
|
+
const mode = input.mode === "allowlist" || input.mode === "denylist" ? input.mode : "all";
|
|
176
|
+
const updatedAt = typeof input.updatedAt === "number" && Number.isFinite(input.updatedAt) ? input.updatedAt : now;
|
|
177
|
+
if (mode === "all") {
|
|
178
|
+
return { mode: "all", toolIds: [], updatedAt };
|
|
179
|
+
}
|
|
180
|
+
return { mode, toolIds: normalizeToolIds(input.toolIds), updatedAt };
|
|
181
|
+
}
|
|
182
|
+
function normalizeToolPolicyForUpdate(input, now = Date.now()) {
|
|
183
|
+
return normalizeToolPolicy(input, now) ?? { mode: "all", toolIds: [], updatedAt: now };
|
|
184
|
+
}
|
|
185
|
+
function isToolAllowed(policy, toolName, serverId) {
|
|
186
|
+
if (!policy || policy.mode === "all") return true;
|
|
187
|
+
if (!serverId) return false;
|
|
188
|
+
const toolId = createToolId(serverId, toolName);
|
|
189
|
+
if (policy.mode === "allowlist") {
|
|
190
|
+
return policy.toolIds.includes(toolId);
|
|
191
|
+
}
|
|
192
|
+
return !policy.toolIds.includes(toolId);
|
|
193
|
+
}
|
|
194
|
+
function assertToolAllowed(policy, toolName, serverId) {
|
|
195
|
+
if (isToolAllowed(policy, toolName, serverId)) return;
|
|
196
|
+
throw new Error(`Tool "${toolName}" is not allowed for this MCP session`);
|
|
197
|
+
}
|
|
198
|
+
function filterToolsByPolicy(tools, policy, serverId) {
|
|
199
|
+
if (!policy || policy.mode === "all") return tools;
|
|
200
|
+
return tools.filter((tool) => isToolAllowed(policy, tool.name, serverId));
|
|
201
|
+
}
|
|
202
|
+
function validateToolPolicyAgainstTools(policy, tools, serverId) {
|
|
203
|
+
if (policy.mode === "all") return;
|
|
204
|
+
if (!serverId) {
|
|
205
|
+
throw new Error("Cannot validate MCP tool policy without a serverId");
|
|
206
|
+
}
|
|
207
|
+
const availableIds = new Set(tools.map((tool) => createToolId(serverId, tool.name)));
|
|
208
|
+
const unknownIds = policy.toolIds.filter((id) => !availableIds.has(id));
|
|
209
|
+
if (unknownIds.length > 0) {
|
|
210
|
+
throw new Error(`Unknown tool id(s) for this MCP session: ${unknownIds.join(", ")}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
162
214
|
// src/server/storage/session-lifecycle.ts
|
|
163
215
|
function resolveSessionExpiresAt(status = "pending", referenceTime = Date.now()) {
|
|
164
216
|
return status === "active" ? null : referenceTime + STATE_EXPIRATION_MS;
|
|
@@ -175,7 +227,8 @@ function normalizeNewSession(session, now = Date.now()) {
|
|
|
175
227
|
status,
|
|
176
228
|
createdAt,
|
|
177
229
|
updatedAt,
|
|
178
|
-
expiresAt: resolveSessionExpiresAt(status, status === "active" ? updatedAt : createdAt)
|
|
230
|
+
expiresAt: resolveSessionExpiresAt(status, status === "active" ? updatedAt : createdAt),
|
|
231
|
+
toolPolicy: normalizeToolPolicy(session.toolPolicy, updatedAt)
|
|
179
232
|
};
|
|
180
233
|
}
|
|
181
234
|
function mergeSessionUpdate(current, data, now = Date.now()) {
|
|
@@ -189,7 +242,8 @@ function mergeSessionUpdate(current, data, now = Date.now()) {
|
|
|
189
242
|
return {
|
|
190
243
|
...updated,
|
|
191
244
|
status,
|
|
192
|
-
expiresAt: resolveSessionExpiresAt(status, updatedAt)
|
|
245
|
+
expiresAt: resolveSessionExpiresAt(status, updatedAt),
|
|
246
|
+
toolPolicy: normalizeToolPolicy(updated.toolPolicy, updatedAt)
|
|
193
247
|
};
|
|
194
248
|
}
|
|
195
249
|
function normalizeStoredSession(session) {
|
|
@@ -202,7 +256,8 @@ function normalizeStoredSession(session) {
|
|
|
202
256
|
status,
|
|
203
257
|
createdAt,
|
|
204
258
|
updatedAt,
|
|
205
|
-
expiresAt
|
|
259
|
+
expiresAt,
|
|
260
|
+
toolPolicy: normalizeToolPolicy(session.toolPolicy, updatedAt)
|
|
206
261
|
};
|
|
207
262
|
}
|
|
208
263
|
function isSessionExpired(session, now = Date.now()) {
|
|
@@ -1060,7 +1115,8 @@ var SupabaseStorageBackend = class {
|
|
|
1060
1115
|
userId: row.user_id,
|
|
1061
1116
|
headers: decryptObject(row.headers),
|
|
1062
1117
|
authUrl: row.auth_url,
|
|
1063
|
-
status: row.status ?? "pending"
|
|
1118
|
+
status: row.status ?? "pending",
|
|
1119
|
+
toolPolicy: normalizeToolPolicy(row.tool_policy)
|
|
1064
1120
|
};
|
|
1065
1121
|
}
|
|
1066
1122
|
mapRowToCredentials(row, userId, sessionId) {
|
|
@@ -1084,7 +1140,7 @@ var SupabaseStorageBackend = class {
|
|
|
1084
1140
|
const createdAt = new Date(session.createdAt || Date.now()).toISOString();
|
|
1085
1141
|
const updatedAt = new Date(session.updatedAt ?? session.createdAt ?? Date.now()).toISOString();
|
|
1086
1142
|
const expiresAt = resolveSessionExpiresAt(status, new Date(createdAt).getTime());
|
|
1087
|
-
const
|
|
1143
|
+
const insertData = {
|
|
1088
1144
|
session_id: sessionId,
|
|
1089
1145
|
user_id: userId,
|
|
1090
1146
|
server_id: session.serverId,
|
|
@@ -1098,7 +1154,12 @@ var SupabaseStorageBackend = class {
|
|
|
1098
1154
|
auth_url: session.authUrl ?? null,
|
|
1099
1155
|
status,
|
|
1100
1156
|
expires_at: expiresAt === null ? null : new Date(expiresAt).toISOString()
|
|
1101
|
-
}
|
|
1157
|
+
};
|
|
1158
|
+
const toolPolicy = normalizeToolPolicy(session.toolPolicy);
|
|
1159
|
+
if (toolPolicy) {
|
|
1160
|
+
insertData.tool_policy = toolPolicy;
|
|
1161
|
+
}
|
|
1162
|
+
const { error } = await this.supabase.from("mcp_sessions").insert(insertData);
|
|
1102
1163
|
if (error) {
|
|
1103
1164
|
if (error.code === "23505") {
|
|
1104
1165
|
throw new Error(`Session ${sessionId} already exists`);
|
|
@@ -1123,6 +1184,7 @@ var SupabaseStorageBackend = class {
|
|
|
1123
1184
|
}
|
|
1124
1185
|
if ("headers" in data) updateData.headers = encryptObject(data.headers);
|
|
1125
1186
|
if ("authUrl" in data) updateData.auth_url = data.authUrl ?? null;
|
|
1187
|
+
if ("toolPolicy" in data) updateData.tool_policy = normalizeToolPolicy(data.toolPolicy);
|
|
1126
1188
|
const shouldUpdateSession = Object.keys(updateData).some((key) => key !== "updated_at");
|
|
1127
1189
|
let updatedRows = null;
|
|
1128
1190
|
if (shouldUpdateSession) {
|
|
@@ -1295,7 +1357,8 @@ var NeonStorageBackend = class {
|
|
|
1295
1357
|
userId: row.user_id,
|
|
1296
1358
|
headers: decryptObject(row.headers),
|
|
1297
1359
|
authUrl: row.auth_url ?? void 0,
|
|
1298
|
-
status: row.status ?? "pending"
|
|
1360
|
+
status: row.status ?? "pending",
|
|
1361
|
+
toolPolicy: normalizeToolPolicy(row.tool_policy)
|
|
1299
1362
|
};
|
|
1300
1363
|
}
|
|
1301
1364
|
mapRowToCredentials(row, userId, sessionId) {
|
|
@@ -1318,42 +1381,48 @@ var NeonStorageBackend = class {
|
|
|
1318
1381
|
const status = session.status ?? "pending";
|
|
1319
1382
|
const createdAt = new Date(session.createdAt || Date.now()).toISOString();
|
|
1320
1383
|
const updatedAt = new Date(session.updatedAt ?? session.createdAt ?? Date.now()).toISOString();
|
|
1321
|
-
const
|
|
1384
|
+
const createdAtMs = new Date(createdAt).getTime();
|
|
1385
|
+
const expiresAt = resolveSessionExpiresAt(status, createdAtMs);
|
|
1386
|
+
const toolPolicy = normalizeToolPolicy(session.toolPolicy, createdAtMs);
|
|
1387
|
+
const columns = [
|
|
1388
|
+
"session_id",
|
|
1389
|
+
"user_id",
|
|
1390
|
+
"server_id",
|
|
1391
|
+
"server_name",
|
|
1392
|
+
"server_url",
|
|
1393
|
+
"transport_type",
|
|
1394
|
+
"callback_url",
|
|
1395
|
+
"created_at",
|
|
1396
|
+
"updated_at",
|
|
1397
|
+
"headers",
|
|
1398
|
+
"auth_url",
|
|
1399
|
+
"status",
|
|
1400
|
+
"expires_at"
|
|
1401
|
+
];
|
|
1402
|
+
const values = [
|
|
1403
|
+
sessionId,
|
|
1404
|
+
userId,
|
|
1405
|
+
session.serverId,
|
|
1406
|
+
session.serverName,
|
|
1407
|
+
session.serverUrl,
|
|
1408
|
+
session.transportType,
|
|
1409
|
+
session.callbackUrl,
|
|
1410
|
+
createdAt,
|
|
1411
|
+
updatedAt,
|
|
1412
|
+
encryptObject(session.headers),
|
|
1413
|
+
session.authUrl ?? null,
|
|
1414
|
+
status,
|
|
1415
|
+
expiresAt === null ? null : new Date(expiresAt).toISOString()
|
|
1416
|
+
];
|
|
1417
|
+
if (toolPolicy) {
|
|
1418
|
+
columns.push("tool_policy");
|
|
1419
|
+
values.push(toolPolicy);
|
|
1420
|
+
}
|
|
1421
|
+
const placeholders = values.map((_, i) => `$${i + 1}`);
|
|
1322
1422
|
try {
|
|
1323
1423
|
await this.sql.query(
|
|
1324
|
-
`INSERT INTO ${this.tableName} (
|
|
1325
|
-
|
|
1326
|
-
user_id,
|
|
1327
|
-
server_id,
|
|
1328
|
-
server_name,
|
|
1329
|
-
server_url,
|
|
1330
|
-
transport_type,
|
|
1331
|
-
callback_url,
|
|
1332
|
-
created_at,
|
|
1333
|
-
updated_at,
|
|
1334
|
-
headers,
|
|
1335
|
-
auth_url,
|
|
1336
|
-
status,
|
|
1337
|
-
expires_at
|
|
1338
|
-
) VALUES (
|
|
1339
|
-
$1, $2, $3, $4, $5, $6, $7, $8,
|
|
1340
|
-
$9, $10, $11, $12, $13
|
|
1341
|
-
)`,
|
|
1342
|
-
[
|
|
1343
|
-
sessionId,
|
|
1344
|
-
userId,
|
|
1345
|
-
session.serverId,
|
|
1346
|
-
session.serverName,
|
|
1347
|
-
session.serverUrl,
|
|
1348
|
-
session.transportType,
|
|
1349
|
-
session.callbackUrl,
|
|
1350
|
-
createdAt,
|
|
1351
|
-
updatedAt,
|
|
1352
|
-
encryptObject(session.headers),
|
|
1353
|
-
session.authUrl ?? null,
|
|
1354
|
-
status,
|
|
1355
|
-
expiresAt === null ? null : new Date(expiresAt).toISOString()
|
|
1356
|
-
]
|
|
1424
|
+
`INSERT INTO ${this.tableName} (${columns.join(", ")}) VALUES (${placeholders.join(", ")})`,
|
|
1425
|
+
values
|
|
1357
1426
|
);
|
|
1358
1427
|
} catch (error) {
|
|
1359
1428
|
if (error.code === "23505") {
|
|
@@ -1370,36 +1439,36 @@ var NeonStorageBackend = class {
|
|
|
1370
1439
|
const updatedSession = { ...currentSession, ...data };
|
|
1371
1440
|
const status = updatedSession.status ?? "pending";
|
|
1372
1441
|
const expiresAt = resolveSessionExpiresAt(status);
|
|
1373
|
-
const shouldUpdateSession = "serverId" in data || "serverName" in data || "serverUrl" in data || "transportType" in data || "callbackUrl" in data || "status" in data || "headers" in data || "authUrl" in data;
|
|
1442
|
+
const shouldUpdateSession = "serverId" in data || "serverName" in data || "serverUrl" in data || "transportType" in data || "callbackUrl" in data || "status" in data || "headers" in data || "authUrl" in data || "toolPolicy" in data;
|
|
1374
1443
|
if (shouldUpdateSession) {
|
|
1444
|
+
const setClauses = [];
|
|
1445
|
+
const values = [];
|
|
1446
|
+
let paramIndex = 1;
|
|
1447
|
+
const addSet = (column, value) => {
|
|
1448
|
+
setClauses.push(`${column} = $${paramIndex++}`);
|
|
1449
|
+
values.push(value);
|
|
1450
|
+
};
|
|
1451
|
+
addSet("server_id", updatedSession.serverId);
|
|
1452
|
+
addSet("server_name", updatedSession.serverName);
|
|
1453
|
+
addSet("server_url", updatedSession.serverUrl);
|
|
1454
|
+
addSet("transport_type", updatedSession.transportType);
|
|
1455
|
+
addSet("callback_url", updatedSession.callbackUrl);
|
|
1456
|
+
addSet("status", status);
|
|
1457
|
+
addSet("headers", encryptObject(updatedSession.headers));
|
|
1458
|
+
addSet("auth_url", updatedSession.authUrl ?? null);
|
|
1459
|
+
addSet("expires_at", expiresAt === null ? null : new Date(expiresAt).toISOString());
|
|
1460
|
+
if ("toolPolicy" in data) {
|
|
1461
|
+
const policyUpdatedAt = updatedSession.updatedAt ?? Date.now();
|
|
1462
|
+
const toolPolicy = normalizeToolPolicy(updatedSession.toolPolicy, policyUpdatedAt) ?? { mode: "all", toolIds: [], updatedAt: policyUpdatedAt };
|
|
1463
|
+
addSet("tool_policy", toolPolicy);
|
|
1464
|
+
}
|
|
1465
|
+
setClauses.push("updated_at = now()");
|
|
1375
1466
|
const updatedRows = await this.sql.query(
|
|
1376
1467
|
`UPDATE ${this.tableName}
|
|
1377
|
-
SET
|
|
1378
|
-
|
|
1379
|
-
server_name = $2,
|
|
1380
|
-
server_url = $3,
|
|
1381
|
-
transport_type = $4,
|
|
1382
|
-
callback_url = $5,
|
|
1383
|
-
status = $6,
|
|
1384
|
-
headers = $7,
|
|
1385
|
-
auth_url = $8,
|
|
1386
|
-
expires_at = $9,
|
|
1387
|
-
updated_at = now()
|
|
1388
|
-
WHERE user_id = $10 AND session_id = $11
|
|
1468
|
+
SET ${setClauses.join(", ")}
|
|
1469
|
+
WHERE user_id = $${paramIndex++} AND session_id = $${paramIndex++}
|
|
1389
1470
|
RETURNING id`,
|
|
1390
|
-
[
|
|
1391
|
-
updatedSession.serverId,
|
|
1392
|
-
updatedSession.serverName,
|
|
1393
|
-
updatedSession.serverUrl,
|
|
1394
|
-
updatedSession.transportType,
|
|
1395
|
-
updatedSession.callbackUrl,
|
|
1396
|
-
status,
|
|
1397
|
-
encryptObject(updatedSession.headers),
|
|
1398
|
-
updatedSession.authUrl ?? null,
|
|
1399
|
-
expiresAt === null ? null : new Date(expiresAt).toISOString(),
|
|
1400
|
-
userId,
|
|
1401
|
-
sessionId
|
|
1402
|
-
]
|
|
1471
|
+
[...values, userId, sessionId]
|
|
1403
1472
|
);
|
|
1404
1473
|
if (updatedRows.length === 0) {
|
|
1405
1474
|
throw new Error(`Session ${sessionId} not found for userId ${userId}`);
|
|
@@ -2314,7 +2383,15 @@ var MCPClient = class {
|
|
|
2314
2383
|
if (!existingSession && this.serverId && this.serverUrl && this.callbackUrl) {
|
|
2315
2384
|
this.createdAt = Date.now();
|
|
2316
2385
|
const updatedAt = this.createdAt;
|
|
2317
|
-
|
|
2386
|
+
this._onObservabilityEvent.fire({
|
|
2387
|
+
type: "mcp:client:session_created",
|
|
2388
|
+
level: "info",
|
|
2389
|
+
message: `Creating pending session ${this.sessionId} for connection setup`,
|
|
2390
|
+
sessionId: this.sessionId,
|
|
2391
|
+
serverId: this.serverId,
|
|
2392
|
+
timestamp: Date.now(),
|
|
2393
|
+
id: nanoid()
|
|
2394
|
+
});
|
|
2318
2395
|
await sessions.create({
|
|
2319
2396
|
sessionId: this.sessionId,
|
|
2320
2397
|
userId: this.userId,
|
|
@@ -2444,7 +2521,15 @@ var MCPClient = class {
|
|
|
2444
2521
|
this.transportType = transportType;
|
|
2445
2522
|
this.emitStateChange("CONNECTED");
|
|
2446
2523
|
this.emitProgress("Connected successfully");
|
|
2447
|
-
|
|
2524
|
+
this._onObservabilityEvent.fire({
|
|
2525
|
+
type: "mcp:client:session_saved",
|
|
2526
|
+
level: "info",
|
|
2527
|
+
message: `Saving active session ${this.sessionId} (connect success)`,
|
|
2528
|
+
sessionId: this.sessionId,
|
|
2529
|
+
serverId: this.serverId,
|
|
2530
|
+
timestamp: Date.now(),
|
|
2531
|
+
id: nanoid()
|
|
2532
|
+
});
|
|
2448
2533
|
await this.saveSession("active");
|
|
2449
2534
|
} catch (error) {
|
|
2450
2535
|
if (error instanceof UnauthorizedError$1 || error instanceof Error && error.message.toLowerCase().includes("unauthorized")) {
|
|
@@ -2461,7 +2546,15 @@ var MCPClient = class {
|
|
|
2461
2546
|
throw new Error(message);
|
|
2462
2547
|
}
|
|
2463
2548
|
this.emitStateChange("AUTHENTICATING");
|
|
2464
|
-
|
|
2549
|
+
this._onObservabilityEvent.fire({
|
|
2550
|
+
type: "mcp:client:session_saved",
|
|
2551
|
+
level: "info",
|
|
2552
|
+
message: `Saving pending OAuth session ${this.sessionId}`,
|
|
2553
|
+
sessionId: this.sessionId,
|
|
2554
|
+
serverId: this.serverId,
|
|
2555
|
+
timestamp: Date.now(),
|
|
2556
|
+
id: nanoid()
|
|
2557
|
+
});
|
|
2465
2558
|
await this.saveSession("pending");
|
|
2466
2559
|
if (this.serverId) {
|
|
2467
2560
|
this._onConnectionEvent.fire({
|
|
@@ -2536,27 +2629,25 @@ var MCPClient = class {
|
|
|
2536
2629
|
this.emitStateChange("AUTHENTICATED");
|
|
2537
2630
|
authenticatedStateEmitted = true;
|
|
2538
2631
|
}
|
|
2539
|
-
this.emitProgress("Creating authenticated client...");
|
|
2540
|
-
this.client = new Client(
|
|
2541
|
-
{
|
|
2542
|
-
name: MCP_CLIENT_NAME,
|
|
2543
|
-
version: MCP_CLIENT_VERSION
|
|
2544
|
-
},
|
|
2545
|
-
{
|
|
2546
|
-
capabilities: {
|
|
2547
|
-
extensions: {
|
|
2548
|
-
"io.modelcontextprotocol/ui": {
|
|
2549
|
-
mimeTypes: ["text/html+mcp"]
|
|
2550
|
-
}
|
|
2551
|
-
}
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2554
|
-
);
|
|
2555
2632
|
this.emitStateChange("CONNECTING");
|
|
2633
|
+
if (this.client.transport) {
|
|
2634
|
+
try {
|
|
2635
|
+
await this.client.close();
|
|
2636
|
+
} catch {
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2556
2639
|
await this.client.connect(this.transport);
|
|
2557
2640
|
this.transportType = currentType;
|
|
2558
2641
|
this.emitStateChange("CONNECTED");
|
|
2559
|
-
|
|
2642
|
+
this._onObservabilityEvent.fire({
|
|
2643
|
+
type: "mcp:client:session_saved",
|
|
2644
|
+
level: "info",
|
|
2645
|
+
message: `Saving active session ${this.sessionId} (OAuth complete)`,
|
|
2646
|
+
sessionId: this.sessionId,
|
|
2647
|
+
serverId: this.serverId,
|
|
2648
|
+
timestamp: Date.now(),
|
|
2649
|
+
id: nanoid()
|
|
2650
|
+
});
|
|
2560
2651
|
await this.saveSession("active");
|
|
2561
2652
|
return;
|
|
2562
2653
|
} catch (error) {
|
|
@@ -2591,24 +2682,28 @@ var MCPClient = class {
|
|
|
2591
2682
|
throw lastError;
|
|
2592
2683
|
}
|
|
2593
2684
|
}
|
|
2685
|
+
/**
|
|
2686
|
+
* Lists all available tools from the connected MCP server without emitting discovery events.
|
|
2687
|
+
* Gateways use this to apply policy before publishing tools to agents or UI state.
|
|
2688
|
+
*/
|
|
2689
|
+
async fetchTools() {
|
|
2690
|
+
const request = {
|
|
2691
|
+
method: "tools/list",
|
|
2692
|
+
params: {}
|
|
2693
|
+
};
|
|
2694
|
+
return await this.withRetry(
|
|
2695
|
+
() => this.client.request(request, ListToolsResultSchema)
|
|
2696
|
+
);
|
|
2697
|
+
}
|
|
2594
2698
|
/**
|
|
2595
2699
|
* Lists all available tools from the connected MCP server
|
|
2596
2700
|
* @returns List of tools with their schemas and descriptions
|
|
2597
2701
|
* @throws {Error} When client is not connected
|
|
2598
2702
|
*/
|
|
2599
2703
|
async listTools() {
|
|
2600
|
-
if (!this.client) {
|
|
2601
|
-
throw new Error("Not connected to server");
|
|
2602
|
-
}
|
|
2603
2704
|
this.emitStateChange("DISCOVERING");
|
|
2604
2705
|
try {
|
|
2605
|
-
const
|
|
2606
|
-
method: "tools/list",
|
|
2607
|
-
params: {}
|
|
2608
|
-
};
|
|
2609
|
-
const result = await this.withRetry(
|
|
2610
|
-
() => this.client.request(request, ListToolsResultSchema)
|
|
2611
|
-
);
|
|
2706
|
+
const result = await this.fetchTools();
|
|
2612
2707
|
if (this.serverId) {
|
|
2613
2708
|
this._onConnectionEvent.fire({
|
|
2614
2709
|
type: "tools_discovered",
|
|
@@ -2637,9 +2732,6 @@ var MCPClient = class {
|
|
|
2637
2732
|
* @throws {Error} When client is not connected
|
|
2638
2733
|
*/
|
|
2639
2734
|
async callTool(toolName, toolArgs) {
|
|
2640
|
-
if (!this.client) {
|
|
2641
|
-
throw new Error("Not connected to server");
|
|
2642
|
-
}
|
|
2643
2735
|
const request = {
|
|
2644
2736
|
method: "tools/call",
|
|
2645
2737
|
params: {
|
|
@@ -2693,9 +2785,6 @@ var MCPClient = class {
|
|
|
2693
2785
|
* @throws {Error} When client is not connected
|
|
2694
2786
|
*/
|
|
2695
2787
|
async listPrompts() {
|
|
2696
|
-
if (!this.client) {
|
|
2697
|
-
throw new Error("Not connected to server");
|
|
2698
|
-
}
|
|
2699
2788
|
this.emitStateChange("DISCOVERING");
|
|
2700
2789
|
try {
|
|
2701
2790
|
const request = {
|
|
@@ -2723,9 +2812,6 @@ var MCPClient = class {
|
|
|
2723
2812
|
* @throws {Error} When client is not connected
|
|
2724
2813
|
*/
|
|
2725
2814
|
async getPrompt(name, args) {
|
|
2726
|
-
if (!this.client) {
|
|
2727
|
-
throw new Error("Not connected to server");
|
|
2728
|
-
}
|
|
2729
2815
|
const request = {
|
|
2730
2816
|
method: "prompts/get",
|
|
2731
2817
|
params: {
|
|
@@ -2743,9 +2829,6 @@ var MCPClient = class {
|
|
|
2743
2829
|
* @throws {Error} When client is not connected
|
|
2744
2830
|
*/
|
|
2745
2831
|
async listResources() {
|
|
2746
|
-
if (!this.client) {
|
|
2747
|
-
throw new Error("Not connected to server");
|
|
2748
|
-
}
|
|
2749
2832
|
this.emitStateChange("DISCOVERING");
|
|
2750
2833
|
try {
|
|
2751
2834
|
const request = {
|
|
@@ -2772,9 +2855,6 @@ var MCPClient = class {
|
|
|
2772
2855
|
* @throws {Error} When client is not connected
|
|
2773
2856
|
*/
|
|
2774
2857
|
async readResource(uri) {
|
|
2775
|
-
if (!this.client) {
|
|
2776
|
-
throw new Error("Not connected to server");
|
|
2777
|
-
}
|
|
2778
2858
|
const request = {
|
|
2779
2859
|
method: "resources/read",
|
|
2780
2860
|
params: {
|
|
@@ -2830,7 +2910,16 @@ var MCPClient = class {
|
|
|
2830
2910
|
try {
|
|
2831
2911
|
await this.ensureSession();
|
|
2832
2912
|
} catch (error) {
|
|
2833
|
-
|
|
2913
|
+
this._onObservabilityEvent.fire({
|
|
2914
|
+
type: "mcp:client:error",
|
|
2915
|
+
level: "warn",
|
|
2916
|
+
message: "Initialization failed during clearSession",
|
|
2917
|
+
sessionId: this.sessionId,
|
|
2918
|
+
serverId: this.serverId,
|
|
2919
|
+
payload: { error: String(error) },
|
|
2920
|
+
timestamp: Date.now(),
|
|
2921
|
+
id: nanoid()
|
|
2922
|
+
});
|
|
2834
2923
|
}
|
|
2835
2924
|
if (this.oauthProvider) {
|
|
2836
2925
|
await this.oauthProvider.invalidateCredentials("all");
|
|
@@ -2941,6 +3030,66 @@ var MCPClient = class {
|
|
|
2941
3030
|
}
|
|
2942
3031
|
};
|
|
2943
3032
|
|
|
3033
|
+
// src/server/mcp/tool-policy-gateway.ts
|
|
3034
|
+
var ToolPolicyGateway = class {
|
|
3035
|
+
constructor(userId, sessionId, client) {
|
|
3036
|
+
this.userId = userId;
|
|
3037
|
+
this.sessionId = sessionId;
|
|
3038
|
+
this.client = client;
|
|
3039
|
+
}
|
|
3040
|
+
isConnected() {
|
|
3041
|
+
return this.client.isConnected();
|
|
3042
|
+
}
|
|
3043
|
+
getServerId() {
|
|
3044
|
+
return this.client.getServerId?.();
|
|
3045
|
+
}
|
|
3046
|
+
getServerName() {
|
|
3047
|
+
return this.client.getServerName?.();
|
|
3048
|
+
}
|
|
3049
|
+
getServerUrl() {
|
|
3050
|
+
return this.client.getServerUrl?.();
|
|
3051
|
+
}
|
|
3052
|
+
getSessionId() {
|
|
3053
|
+
return this.client.getSessionId?.() ?? this.sessionId;
|
|
3054
|
+
}
|
|
3055
|
+
async listTools() {
|
|
3056
|
+
const session = await this.getSession();
|
|
3057
|
+
const result = await this.client.fetchTools();
|
|
3058
|
+
const tools = this.filterTools(session, result.tools);
|
|
3059
|
+
return {
|
|
3060
|
+
...result,
|
|
3061
|
+
tools
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
3064
|
+
async listAllTools() {
|
|
3065
|
+
return await this.client.fetchTools();
|
|
3066
|
+
}
|
|
3067
|
+
async callTool(name, args) {
|
|
3068
|
+
const session = await this.getSession();
|
|
3069
|
+
this.assertAllowed(session, name);
|
|
3070
|
+
return await this.client.callTool(name, args);
|
|
3071
|
+
}
|
|
3072
|
+
filterTools(session, tools) {
|
|
3073
|
+
return filterToolsByPolicy(tools, session.toolPolicy, this.getPolicyServerId(session));
|
|
3074
|
+
}
|
|
3075
|
+
assertAllowed(session, toolName) {
|
|
3076
|
+
assertToolAllowed(session.toolPolicy, toolName, this.getPolicyServerId(session));
|
|
3077
|
+
}
|
|
3078
|
+
async getSession() {
|
|
3079
|
+
const session = await sessions.get(this.userId, this.sessionId);
|
|
3080
|
+
if (!session) {
|
|
3081
|
+
throw new Error("Session not found");
|
|
3082
|
+
}
|
|
3083
|
+
return session;
|
|
3084
|
+
}
|
|
3085
|
+
getPolicyServerId(session) {
|
|
3086
|
+
return this.client.getServerId?.() ?? session.serverId;
|
|
3087
|
+
}
|
|
3088
|
+
};
|
|
3089
|
+
function createToolPolicyGateway(userId, sessionId, client) {
|
|
3090
|
+
return new ToolPolicyGateway(userId, sessionId, client);
|
|
3091
|
+
}
|
|
3092
|
+
|
|
2944
3093
|
// src/server/mcp/multi-session-client.ts
|
|
2945
3094
|
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
2946
3095
|
var DEFAULT_MAX_RETRIES = 2;
|
|
@@ -3006,7 +3155,9 @@ var MultiSessionClient = class {
|
|
|
3006
3155
|
* or to route a tool call to the right client by `serverId`.
|
|
3007
3156
|
*/
|
|
3008
3157
|
getClients() {
|
|
3009
|
-
return this.clients
|
|
3158
|
+
return this.clients.map(
|
|
3159
|
+
(client) => createToolPolicyGateway(this.userId, client.getSessionId(), client)
|
|
3160
|
+
);
|
|
3010
3161
|
}
|
|
3011
3162
|
/**
|
|
3012
3163
|
* Removes and disconnects a single session by ID.
|
|
@@ -3241,6 +3392,12 @@ var SSEConnectionManager = class {
|
|
|
3241
3392
|
case "listTools":
|
|
3242
3393
|
result = await this.listTools(request.params);
|
|
3243
3394
|
break;
|
|
3395
|
+
case "setToolPolicy":
|
|
3396
|
+
result = await this.setToolPolicy(request.params);
|
|
3397
|
+
break;
|
|
3398
|
+
case "getToolPolicy":
|
|
3399
|
+
result = await this.getToolPolicy(request.params);
|
|
3400
|
+
break;
|
|
3244
3401
|
case "callTool":
|
|
3245
3402
|
result = await this.callTool(request.params);
|
|
3246
3403
|
break;
|
|
@@ -3297,7 +3454,8 @@ var SSEConnectionManager = class {
|
|
|
3297
3454
|
transport: s.transportType,
|
|
3298
3455
|
createdAt: s.createdAt,
|
|
3299
3456
|
updatedAt: s.updatedAt ?? s.createdAt,
|
|
3300
|
-
status: s.status ?? "pending"
|
|
3457
|
+
status: s.status ?? "pending",
|
|
3458
|
+
toolPolicy: s.toolPolicy
|
|
3301
3459
|
}))
|
|
3302
3460
|
};
|
|
3303
3461
|
}
|
|
@@ -3307,7 +3465,7 @@ var SSEConnectionManager = class {
|
|
|
3307
3465
|
async connect(params) {
|
|
3308
3466
|
const { serverName, serverUrl, callbackUrl, transportType } = params;
|
|
3309
3467
|
const headers = normalizeHeaders(params.headers);
|
|
3310
|
-
const serverId = params.serverId && params.serverId.length <= 12 ? params.serverId :
|
|
3468
|
+
const serverId = params.serverId && params.serverId.length <= 12 ? params.serverId : generateServerId();
|
|
3311
3469
|
const existingSessions = await sessions.list(this.userId);
|
|
3312
3470
|
const duplicate = existingSessions.find(
|
|
3313
3471
|
(s) => s.serverId === serverId || s.serverUrl === serverUrl
|
|
@@ -3345,7 +3503,7 @@ var SSEConnectionManager = class {
|
|
|
3345
3503
|
this.sendEvent(event);
|
|
3346
3504
|
});
|
|
3347
3505
|
await client.connect();
|
|
3348
|
-
await
|
|
3506
|
+
await this.listPolicyFilteredTools(sessionId);
|
|
3349
3507
|
return {
|
|
3350
3508
|
sessionId,
|
|
3351
3509
|
success: true
|
|
@@ -3416,22 +3574,98 @@ var SSEConnectionManager = class {
|
|
|
3416
3574
|
this.clients.set(sessionId, client);
|
|
3417
3575
|
return client;
|
|
3418
3576
|
}
|
|
3577
|
+
async listPolicyFilteredTools(sessionId) {
|
|
3578
|
+
const session = await sessions.get(this.userId, sessionId);
|
|
3579
|
+
if (!session) {
|
|
3580
|
+
throw new Error("Session not found");
|
|
3581
|
+
}
|
|
3582
|
+
const client = await this.getOrCreateClient(sessionId);
|
|
3583
|
+
const gateway = createToolPolicyGateway(this.userId, sessionId, client);
|
|
3584
|
+
const result = await gateway.listTools();
|
|
3585
|
+
this.emitConnectionEvent({
|
|
3586
|
+
type: "tools_discovered",
|
|
3587
|
+
sessionId,
|
|
3588
|
+
serverId: session.serverId ?? "unknown",
|
|
3589
|
+
toolCount: result.tools.length,
|
|
3590
|
+
tools: result.tools,
|
|
3591
|
+
timestamp: Date.now()
|
|
3592
|
+
});
|
|
3593
|
+
return { session, result };
|
|
3594
|
+
}
|
|
3419
3595
|
/**
|
|
3420
3596
|
* List tools from a session
|
|
3421
3597
|
*/
|
|
3422
3598
|
async listTools(params) {
|
|
3423
3599
|
const { sessionId } = params;
|
|
3424
|
-
const
|
|
3425
|
-
const result = await client.listTools();
|
|
3600
|
+
const { result } = await this.listPolicyFilteredTools(sessionId);
|
|
3426
3601
|
return { tools: result.tools };
|
|
3427
3602
|
}
|
|
3603
|
+
/**
|
|
3604
|
+
* Get all raw tools with effective access state for management UI.
|
|
3605
|
+
*/
|
|
3606
|
+
async getToolPolicy(params) {
|
|
3607
|
+
const { sessionId } = params;
|
|
3608
|
+
const session = await sessions.get(this.userId, sessionId);
|
|
3609
|
+
if (!session) {
|
|
3610
|
+
throw new Error("Session not found");
|
|
3611
|
+
}
|
|
3612
|
+
const client = await this.getOrCreateClient(sessionId);
|
|
3613
|
+
const allTools = await client.fetchTools();
|
|
3614
|
+
const toolPolicy = session.toolPolicy ?? {
|
|
3615
|
+
mode: "all",
|
|
3616
|
+
toolIds: [],
|
|
3617
|
+
updatedAt: session.updatedAt ?? session.createdAt
|
|
3618
|
+
};
|
|
3619
|
+
const serverId = session.serverId ?? "unknown";
|
|
3620
|
+
const tools = allTools.tools.map((tool) => ({
|
|
3621
|
+
...tool,
|
|
3622
|
+
toolId: createToolId(serverId, tool.name),
|
|
3623
|
+
allowed: isToolAllowed(toolPolicy, tool.name, session.serverId)
|
|
3624
|
+
}));
|
|
3625
|
+
return {
|
|
3626
|
+
toolPolicy,
|
|
3627
|
+
tools,
|
|
3628
|
+
toolCount: tools.length,
|
|
3629
|
+
allowedToolCount: tools.filter((tool) => tool.allowed).length
|
|
3630
|
+
};
|
|
3631
|
+
}
|
|
3632
|
+
/**
|
|
3633
|
+
* Update per-session tool access policy.
|
|
3634
|
+
*/
|
|
3635
|
+
async setToolPolicy(params) {
|
|
3636
|
+
const { sessionId } = params;
|
|
3637
|
+
const session = await sessions.get(this.userId, sessionId);
|
|
3638
|
+
if (!session) {
|
|
3639
|
+
throw new Error("Session not found");
|
|
3640
|
+
}
|
|
3641
|
+
const client = await this.getOrCreateClient(sessionId);
|
|
3642
|
+
const allTools = await client.fetchTools();
|
|
3643
|
+
const toolPolicy = normalizeToolPolicyForUpdate(params.toolPolicy);
|
|
3644
|
+
validateToolPolicyAgainstTools(toolPolicy, allTools.tools, session.serverId);
|
|
3645
|
+
await sessions.update(this.userId, sessionId, { toolPolicy });
|
|
3646
|
+
const filteredTools = createToolPolicyGateway(this.userId, sessionId, client).filterTools({ ...session, toolPolicy }, allTools.tools);
|
|
3647
|
+
this.emitConnectionEvent({
|
|
3648
|
+
type: "tools_discovered",
|
|
3649
|
+
sessionId,
|
|
3650
|
+
serverId: session.serverId ?? "unknown",
|
|
3651
|
+
toolCount: filteredTools.length,
|
|
3652
|
+
tools: filteredTools,
|
|
3653
|
+
timestamp: Date.now()
|
|
3654
|
+
});
|
|
3655
|
+
return {
|
|
3656
|
+
success: true,
|
|
3657
|
+
toolPolicy,
|
|
3658
|
+
tools: filteredTools,
|
|
3659
|
+
toolCount: filteredTools.length
|
|
3660
|
+
};
|
|
3661
|
+
}
|
|
3428
3662
|
/**
|
|
3429
3663
|
* Call a tool on the MCP server
|
|
3430
3664
|
*/
|
|
3431
3665
|
async callTool(params) {
|
|
3432
3666
|
const { sessionId, toolName, toolArgs } = params;
|
|
3433
3667
|
const client = await this.getOrCreateClient(sessionId);
|
|
3434
|
-
const result = await client.callTool(toolName, toolArgs);
|
|
3668
|
+
const result = await createToolPolicyGateway(this.userId, sessionId, client).callTool(toolName, toolArgs);
|
|
3435
3669
|
const meta = result._meta || {};
|
|
3436
3670
|
return {
|
|
3437
3671
|
...result,
|
|
@@ -3481,7 +3715,7 @@ var SSEConnectionManager = class {
|
|
|
3481
3715
|
client.onObservabilityEvent((event) => this.sendEvent(event));
|
|
3482
3716
|
await client.connect();
|
|
3483
3717
|
this.clients.set(sessionId, client);
|
|
3484
|
-
const tools = await
|
|
3718
|
+
const { result: tools } = await this.listPolicyFilteredTools(sessionId);
|
|
3485
3719
|
return { success: true, toolCount: tools.tools.length };
|
|
3486
3720
|
} catch (error) {
|
|
3487
3721
|
this.emitConnectionEvent({
|
|
@@ -3529,7 +3763,7 @@ var SSEConnectionManager = class {
|
|
|
3529
3763
|
client.onConnectionEvent((event) => this.emitConnectionEvent(event));
|
|
3530
3764
|
await client.finishAuth(code, oauthState);
|
|
3531
3765
|
this.clients.set(sessionId, client);
|
|
3532
|
-
const tools = await
|
|
3766
|
+
const { result: tools } = await this.listPolicyFilteredTools(sessionId);
|
|
3533
3767
|
return { success: true, toolCount: tools.tools.length };
|
|
3534
3768
|
} catch (error) {
|
|
3535
3769
|
this.emitConnectionEvent({
|