mcp-use 1.10.6 → 1.11.0-canary.11
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 +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/{chunk-KIWNNI6F.js → chunk-2NE5H4KG.js} +16 -2
- package/dist/{chunk-D5WOXLJ2.js → chunk-4QWS5ME6.js} +171 -11
- package/dist/chunk-7P2EMREO.js +101 -0
- package/dist/chunk-7PSUUT4A.js +1055 -0
- package/dist/{chunk-44DFBJUL.js → chunk-BOCIQYWG.js} +196 -496
- package/dist/chunk-BPZJIV4V.js +1873 -0
- package/dist/{chunk-EEUJZMOP.js → chunk-CVKKDXI3.js} +1 -1
- package/dist/chunk-DPK5NHDR.js +12 -0
- package/dist/{chunk-FDKY2O5P.js → chunk-EHCLF3JO.js} +443 -969
- package/dist/{chunk-34R6SIER.js → chunk-FRUZDWXH.js} +1 -1
- package/dist/chunk-GXNAXUDI.js +0 -0
- package/dist/{chunk-JH3ZOGLI.js → chunk-J3WTIYVV.js} +2 -2
- package/dist/{chunk-CPG2WZUL.js → chunk-JRGQRPTN.js} +1 -1
- package/dist/chunk-MFSO5PUW.js +1049 -0
- package/dist/chunk-SQSJ5NKY.js +1055 -0
- package/dist/chunk-T4EDAWDS.js +2638 -0
- package/dist/chunk-UWWLWLS2.js +62 -0
- package/dist/{chunk-BLWPCOUZ.js → chunk-V4YUDB7N.js} +3 -8
- package/dist/index.cjs +5065 -4608
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -1061
- package/dist/{langfuse-N5Y5BSXK.js → langfuse-74RGPTAH.js} +2 -2
- package/dist/notifications-FLGIFS56.js +9 -0
- package/dist/src/adapters/base.d.ts +44 -0
- package/dist/src/adapters/base.d.ts.map +1 -1
- package/dist/src/adapters/index.cjs +1346 -0
- package/dist/src/adapters/index.js +11 -0
- package/dist/src/adapters/langchain_adapter.d.ts +12 -1
- package/dist/src/adapters/langchain_adapter.d.ts.map +1 -1
- package/dist/src/agents/index.cjs +3141 -159
- package/dist/src/agents/index.d.ts +2 -0
- package/dist/src/agents/index.d.ts.map +1 -1
- package/dist/src/agents/index.js +12 -8
- package/dist/src/agents/mcp_agent.d.ts +59 -37
- package/dist/src/agents/mcp_agent.d.ts.map +1 -1
- package/dist/src/agents/remote.d.ts +25 -0
- package/dist/src/agents/remote.d.ts.map +1 -1
- package/dist/src/agents/types.d.ts +76 -0
- package/dist/src/agents/types.d.ts.map +1 -1
- package/dist/src/agents/utils/index.d.ts +1 -0
- package/dist/src/agents/utils/index.d.ts.map +1 -1
- package/dist/src/agents/utils/llm_provider.d.ts +53 -0
- package/dist/src/agents/utils/llm_provider.d.ts.map +1 -0
- package/dist/src/browser.cjs +1856 -423
- package/dist/src/browser.d.ts +1 -2
- package/dist/src/browser.d.ts.map +1 -1
- package/dist/src/browser.js +30 -19
- package/dist/src/client/base.d.ts +1 -0
- package/dist/src/client/base.d.ts.map +1 -1
- package/dist/src/client/browser.d.ts +2 -2
- package/dist/src/client/browser.d.ts.map +1 -1
- package/dist/src/client/prompts.cjs +1 -1
- package/dist/src/client/prompts.js +5 -4
- package/dist/src/client.cjs +3788 -0
- package/dist/src/client.d.ts +2 -0
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +23 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/connectors/base.d.ts +8 -0
- package/dist/src/connectors/base.d.ts.map +1 -1
- package/dist/src/connectors/index.d.ts +0 -1
- package/dist/src/connectors/index.d.ts.map +1 -1
- package/dist/src/managers/server_manager.d.ts.map +1 -1
- package/dist/src/managers/tools/connect_mcp_server.d.ts.map +1 -1
- package/dist/src/react/index.cjs +259 -298
- package/dist/src/react/index.js +9 -8
- package/dist/src/react/types.d.ts +42 -4
- package/dist/src/react/types.d.ts.map +1 -1
- package/dist/src/react/useMcp.d.ts.map +1 -1
- package/dist/src/react/useWidget.d.ts +11 -7
- package/dist/src/react/useWidget.d.ts.map +1 -1
- package/dist/src/react/widget-types.d.ts +6 -2
- package/dist/src/react/widget-types.d.ts.map +1 -1
- package/dist/src/server/endpoints/mount-mcp.d.ts.map +1 -1
- package/dist/src/server/index.cjs +1288 -140
- package/dist/src/server/index.d.ts +2 -0
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/index.js +1183 -102
- package/dist/src/server/mcp-server.d.ts +5 -1
- package/dist/src/server/mcp-server.d.ts.map +1 -1
- package/dist/src/server/notifications/index.d.ts +1 -1
- package/dist/src/server/notifications/index.d.ts.map +1 -1
- package/dist/src/server/notifications/notification-registration.d.ts +51 -0
- package/dist/src/server/notifications/notification-registration.d.ts.map +1 -1
- package/dist/src/server/sessions/index.d.ts +3 -1
- package/dist/src/server/sessions/index.d.ts.map +1 -1
- package/dist/src/server/sessions/session-manager.d.ts +36 -19
- package/dist/src/server/sessions/session-manager.d.ts.map +1 -1
- package/dist/src/server/sessions/stores/filesystem.d.ts +121 -0
- package/dist/src/server/sessions/stores/filesystem.d.ts.map +1 -0
- package/dist/src/server/sessions/stores/index.d.ts +94 -0
- package/dist/src/server/sessions/stores/index.d.ts.map +1 -0
- package/dist/src/server/sessions/stores/memory.d.ts +82 -0
- package/dist/src/server/sessions/stores/memory.d.ts.map +1 -0
- package/dist/src/server/sessions/stores/redis.d.ts +164 -0
- package/dist/src/server/sessions/stores/redis.d.ts.map +1 -0
- package/dist/src/server/sessions/streams/index.d.ts +77 -0
- package/dist/src/server/sessions/streams/index.d.ts.map +1 -0
- package/dist/src/server/sessions/streams/memory.d.ts +76 -0
- package/dist/src/server/sessions/streams/memory.d.ts.map +1 -0
- package/dist/src/server/sessions/streams/redis.d.ts +146 -0
- package/dist/src/server/sessions/streams/redis.d.ts.map +1 -0
- package/dist/src/server/types/common.d.ts +105 -28
- package/dist/src/server/types/common.d.ts.map +1 -1
- package/dist/src/server/types/resource.d.ts +16 -0
- package/dist/src/server/types/resource.d.ts.map +1 -1
- package/dist/src/server/types/widget.d.ts +21 -2
- package/dist/src/server/types/widget.d.ts.map +1 -1
- package/dist/src/server/utils/response-helpers.d.ts +12 -6
- package/dist/src/server/utils/response-helpers.d.ts.map +1 -1
- package/dist/src/server/widgets/index.d.ts +1 -1
- package/dist/src/server/widgets/index.d.ts.map +1 -1
- package/dist/src/server/widgets/mount-widgets-dev.d.ts.map +1 -1
- package/dist/src/server/widgets/setup-widget-routes.d.ts.map +1 -1
- package/dist/src/server/widgets/ui-resource-registration.d.ts.map +1 -1
- package/dist/src/server/widgets/widget-helpers.d.ts +22 -0
- package/dist/src/server/widgets/widget-helpers.d.ts.map +1 -1
- package/dist/src/server/widgets/widget-types.d.ts +2 -0
- package/dist/src/server/widgets/widget-types.d.ts.map +1 -1
- package/dist/src/session.d.ts +16 -2
- package/dist/src/session.d.ts.map +1 -1
- package/dist/src/task_managers/index.d.ts +10 -1
- package/dist/src/task_managers/index.d.ts.map +1 -1
- package/dist/src/task_managers/sse.d.ts +34 -1
- package/dist/src/task_managers/sse.d.ts.map +1 -1
- package/dist/src/task_managers/streamable_http.d.ts +8 -2
- package/dist/src/task_managers/streamable_http.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.d.ts.map +1 -1
- package/dist/{tool-execution-helpers-4X6A63AS.js → tool-execution-helpers-3BYYGIA5.js} +3 -3
- package/dist/tsup.config.d.ts.map +1 -1
- package/package.json +47 -14
- package/dist/src/connectors/websocket.d.ts +0 -38
- package/dist/src/connectors/websocket.d.ts.map +0 -1
- package/dist/src/task_managers/websocket.d.ts +0 -18
- package/dist/src/task_managers/websocket.d.ts.map +0 -1
- /package/dist/{chunk-EW4MJSHA.js → chunk-LGDFGYRL.js} +0 -0
|
@@ -413,8 +413,8 @@ var init_runtime = __esm({
|
|
|
413
413
|
if (isDeno) {
|
|
414
414
|
return await globalThis.Deno.readTextFile(path);
|
|
415
415
|
}
|
|
416
|
-
const { readFileSync } = await import("fs");
|
|
417
|
-
const result =
|
|
416
|
+
const { readFileSync: readFileSync2 } = await import("fs");
|
|
417
|
+
const result = readFileSync2(path, encoding);
|
|
418
418
|
return typeof result === "string" ? result : result.toString(encoding);
|
|
419
419
|
},
|
|
420
420
|
async readFile(path) {
|
|
@@ -422,8 +422,8 @@ var init_runtime = __esm({
|
|
|
422
422
|
const data = await globalThis.Deno.readFile(path);
|
|
423
423
|
return data.buffer;
|
|
424
424
|
}
|
|
425
|
-
const { readFileSync } = await import("fs");
|
|
426
|
-
const buffer =
|
|
425
|
+
const { readFileSync: readFileSync2 } = await import("fs");
|
|
426
|
+
const buffer = readFileSync2(path);
|
|
427
427
|
return buffer.buffer.slice(
|
|
428
428
|
buffer.byteOffset,
|
|
429
429
|
buffer.byteOffset + buffer.byteLength
|
|
@@ -438,8 +438,8 @@ var init_runtime = __esm({
|
|
|
438
438
|
return false;
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
|
-
const { existsSync } = await import("fs");
|
|
442
|
-
return
|
|
441
|
+
const { existsSync: existsSync2 } = await import("fs");
|
|
442
|
+
return existsSync2(path);
|
|
443
443
|
},
|
|
444
444
|
async readdirSync(path) {
|
|
445
445
|
if (isDeno) {
|
|
@@ -643,7 +643,7 @@ var init_logging = __esm({
|
|
|
643
643
|
timestamp({ format: "HH:mm:ss" }),
|
|
644
644
|
this.getFormatter()
|
|
645
645
|
),
|
|
646
|
-
transports: []
|
|
646
|
+
transports: [new winston.transports.Console()]
|
|
647
647
|
});
|
|
648
648
|
}
|
|
649
649
|
return this.instances[name];
|
|
@@ -761,7 +761,7 @@ var VERSION;
|
|
|
761
761
|
var init_version = __esm({
|
|
762
762
|
"src/version.ts"() {
|
|
763
763
|
"use strict";
|
|
764
|
-
VERSION = "1.
|
|
764
|
+
VERSION = "1.11.0-canary.11";
|
|
765
765
|
__name(getPackageVersion, "getPackageVersion");
|
|
766
766
|
}
|
|
767
767
|
});
|
|
@@ -2098,10 +2098,84 @@ var init_conversion2 = __esm({
|
|
|
2098
2098
|
}
|
|
2099
2099
|
});
|
|
2100
2100
|
|
|
2101
|
+
// src/server/utils/jsonrpc-helpers.ts
|
|
2102
|
+
function createNotification(method, params) {
|
|
2103
|
+
return {
|
|
2104
|
+
jsonrpc: "2.0",
|
|
2105
|
+
method,
|
|
2106
|
+
...params && { params }
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
function createRequest(id, method, params) {
|
|
2110
|
+
return {
|
|
2111
|
+
jsonrpc: "2.0",
|
|
2112
|
+
id,
|
|
2113
|
+
method,
|
|
2114
|
+
...params && { params }
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
var init_jsonrpc_helpers = __esm({
|
|
2118
|
+
"src/server/utils/jsonrpc-helpers.ts"() {
|
|
2119
|
+
"use strict";
|
|
2120
|
+
__name(createNotification, "createNotification");
|
|
2121
|
+
__name(createRequest, "createRequest");
|
|
2122
|
+
}
|
|
2123
|
+
});
|
|
2124
|
+
|
|
2125
|
+
// src/server/sessions/notifications.ts
|
|
2126
|
+
var notifications_exports = {};
|
|
2127
|
+
__export(notifications_exports, {
|
|
2128
|
+
sendNotificationToAll: () => sendNotificationToAll,
|
|
2129
|
+
sendNotificationToSession: () => sendNotificationToSession
|
|
2130
|
+
});
|
|
2131
|
+
async function sendNotificationToAll(sessions, method, params) {
|
|
2132
|
+
const notification = createNotification(method, params);
|
|
2133
|
+
for (const [sessionId, session] of sessions.entries()) {
|
|
2134
|
+
try {
|
|
2135
|
+
await session.transport.send(notification);
|
|
2136
|
+
} catch (error2) {
|
|
2137
|
+
console.warn(
|
|
2138
|
+
`[MCP] Failed to send notification to session ${sessionId}:`,
|
|
2139
|
+
error2
|
|
2140
|
+
);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
async function sendNotificationToSession(sessions, sessionId, method, params) {
|
|
2145
|
+
const session = sessions.get(sessionId);
|
|
2146
|
+
if (!session) {
|
|
2147
|
+
return false;
|
|
2148
|
+
}
|
|
2149
|
+
const notification = createNotification(method, params);
|
|
2150
|
+
try {
|
|
2151
|
+
await session.transport.send(notification);
|
|
2152
|
+
return true;
|
|
2153
|
+
} catch (error2) {
|
|
2154
|
+
console.warn(
|
|
2155
|
+
`[MCP] Failed to send notification to session ${sessionId}:`,
|
|
2156
|
+
error2
|
|
2157
|
+
);
|
|
2158
|
+
return false;
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
var init_notifications = __esm({
|
|
2162
|
+
"src/server/sessions/notifications.ts"() {
|
|
2163
|
+
"use strict";
|
|
2164
|
+
init_jsonrpc_helpers();
|
|
2165
|
+
__name(sendNotificationToAll, "sendNotificationToAll");
|
|
2166
|
+
__name(sendNotificationToSession, "sendNotificationToSession");
|
|
2167
|
+
}
|
|
2168
|
+
});
|
|
2169
|
+
|
|
2101
2170
|
// src/server/index.ts
|
|
2102
2171
|
var server_exports = {};
|
|
2103
2172
|
__export(server_exports, {
|
|
2173
|
+
FileSystemSessionStore: () => FileSystemSessionStore,
|
|
2174
|
+
InMemorySessionStore: () => InMemorySessionStore,
|
|
2175
|
+
InMemoryStreamManager: () => InMemoryStreamManager,
|
|
2104
2176
|
MCPServer: () => MCPServer,
|
|
2177
|
+
RedisSessionStore: () => RedisSessionStore,
|
|
2178
|
+
RedisStreamManager: () => RedisStreamManager,
|
|
2105
2179
|
VERSION: () => VERSION,
|
|
2106
2180
|
adaptConnectMiddleware: () => adaptConnectMiddleware,
|
|
2107
2181
|
adaptMiddleware: () => adaptMiddleware,
|
|
@@ -2427,17 +2501,23 @@ function binary(base64Data, mimeType) {
|
|
|
2427
2501
|
}
|
|
2428
2502
|
__name(binary, "binary");
|
|
2429
2503
|
function widget(config) {
|
|
2430
|
-
const
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
}
|
|
2437
|
-
],
|
|
2438
|
-
// structuredContent will be injected as window.openai.toolOutput by Apps SDK
|
|
2439
|
-
structuredContent: data
|
|
2504
|
+
const props = config.props || config.data || {};
|
|
2505
|
+
const { output, message } = config;
|
|
2506
|
+
const finalContent = message ? [{ type: "text", text: message }] : Array.isArray(output?.content) && output.content.length > 0 ? output.content : [{ type: "text", text: "" }];
|
|
2507
|
+
const meta = {
|
|
2508
|
+
...output?._meta || {},
|
|
2509
|
+
"mcp-use/props": props
|
|
2440
2510
|
};
|
|
2511
|
+
const result = {
|
|
2512
|
+
content: finalContent,
|
|
2513
|
+
_meta: meta
|
|
2514
|
+
};
|
|
2515
|
+
if (output?.structuredContent) {
|
|
2516
|
+
result.structuredContent = output.structuredContent;
|
|
2517
|
+
} else if (Object.keys(props).length > 0) {
|
|
2518
|
+
result.structuredContent = props;
|
|
2519
|
+
}
|
|
2520
|
+
return result;
|
|
2441
2521
|
}
|
|
2442
2522
|
__name(widget, "widget");
|
|
2443
2523
|
function mix(...results) {
|
|
@@ -3107,7 +3187,7 @@ function processWidgetHtml(html2, widgetName, baseUrl) {
|
|
|
3107
3187
|
}
|
|
3108
3188
|
__name(processWidgetHtml, "processWidgetHtml");
|
|
3109
3189
|
function createWidgetRegistration(widgetName, metadata, html2, serverConfig, isDev = false) {
|
|
3110
|
-
const props = metadata.inputs || {};
|
|
3190
|
+
const props = metadata.props || metadata.inputs || metadata.schema || {};
|
|
3111
3191
|
const description = metadata.description || `Widget: ${widgetName}`;
|
|
3112
3192
|
const title = metadata.title || widgetName;
|
|
3113
3193
|
const exposeAsTool = metadata.exposeAsTool !== void 0 ? metadata.exposeAsTool : true;
|
|
@@ -3249,6 +3329,33 @@ function setupPublicRoutes(app, useDistDirectory = false) {
|
|
|
3249
3329
|
});
|
|
3250
3330
|
}
|
|
3251
3331
|
__name(setupPublicRoutes, "setupPublicRoutes");
|
|
3332
|
+
function setupFaviconRoute(app, faviconPath, useDistDirectory = false) {
|
|
3333
|
+
if (!faviconPath) {
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
app.get("/favicon.ico", async (c) => {
|
|
3337
|
+
const basePath = useDistDirectory ? "dist/public" : "public";
|
|
3338
|
+
const fullPath = pathHelpers.join(getCwd(), basePath, faviconPath);
|
|
3339
|
+
try {
|
|
3340
|
+
if (await fsHelpers.existsSync(fullPath)) {
|
|
3341
|
+
const content = await fsHelpers.readFile(fullPath);
|
|
3342
|
+
const contentType = getContentType(faviconPath);
|
|
3343
|
+
return new Response(content, {
|
|
3344
|
+
status: 200,
|
|
3345
|
+
headers: {
|
|
3346
|
+
"Content-Type": contentType,
|
|
3347
|
+
"Cache-Control": "public, max-age=31536000"
|
|
3348
|
+
// Cache for 1 year
|
|
3349
|
+
}
|
|
3350
|
+
});
|
|
3351
|
+
}
|
|
3352
|
+
return c.notFound();
|
|
3353
|
+
} catch {
|
|
3354
|
+
return c.notFound();
|
|
3355
|
+
}
|
|
3356
|
+
});
|
|
3357
|
+
}
|
|
3358
|
+
__name(setupFaviconRoute, "setupFaviconRoute");
|
|
3252
3359
|
|
|
3253
3360
|
// src/server/widgets/mount-widgets-dev.ts
|
|
3254
3361
|
var TMP_MCP_USE_DIR = ".mcp-use";
|
|
@@ -3298,6 +3405,19 @@ async function mountWidgetsDev(app, serverConfig, registerWidget, options) {
|
|
|
3298
3405
|
return;
|
|
3299
3406
|
}
|
|
3300
3407
|
const tempDir = pathHelpers.join(getCwd(), TMP_MCP_USE_DIR);
|
|
3408
|
+
try {
|
|
3409
|
+
await fs.access(tempDir);
|
|
3410
|
+
const currentWidgetNames = new Set(entries.map((e) => e.name));
|
|
3411
|
+
const existingDirs = await fs.readdir(tempDir, { withFileTypes: true });
|
|
3412
|
+
for (const dirent of existingDirs) {
|
|
3413
|
+
if (dirent.isDirectory() && !currentWidgetNames.has(dirent.name)) {
|
|
3414
|
+
const staleDir = pathHelpers.join(tempDir, dirent.name);
|
|
3415
|
+
await fs.rm(staleDir, { recursive: true, force: true });
|
|
3416
|
+
console.log(`[WIDGETS] Cleaned up stale widget: ${dirent.name}`);
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
} catch {
|
|
3420
|
+
}
|
|
3301
3421
|
await fs.mkdir(tempDir, { recursive: true }).catch(() => {
|
|
3302
3422
|
});
|
|
3303
3423
|
let createServer;
|
|
@@ -3335,10 +3455,13 @@ async function mountWidgetsDev(app, serverConfig, registerWidget, options) {
|
|
|
3335
3455
|
await fs.mkdir(widgetTempDir, { recursive: true });
|
|
3336
3456
|
const resourcesPath = pathHelpers.join(getCwd(), resourcesDir);
|
|
3337
3457
|
const relativeResourcesPath = pathHelpers.relative(widgetTempDir, resourcesPath).replace(/\\/g, "/");
|
|
3458
|
+
const mcpUsePath = pathHelpers.join(getCwd(), "node_modules", "mcp-use");
|
|
3459
|
+
const relativeMcpUsePath = pathHelpers.relative(widgetTempDir, mcpUsePath).replace(/\\/g, "/");
|
|
3338
3460
|
const cssContent = `@import "tailwindcss";
|
|
3339
3461
|
|
|
3340
|
-
/* Configure Tailwind to scan the resources directory */
|
|
3462
|
+
/* Configure Tailwind to scan the resources directory and mcp-use package */
|
|
3341
3463
|
@source "${relativeResourcesPath}";
|
|
3464
|
+
@source "${relativeMcpUsePath}/**/*.{ts,tsx,js,jsx}";
|
|
3342
3465
|
`;
|
|
3343
3466
|
await fs.writeFile(
|
|
3344
3467
|
pathHelpers.join(widgetTempDir, "styles.css"),
|
|
@@ -3361,7 +3484,8 @@ if (container && Component) {
|
|
|
3361
3484
|
<head>
|
|
3362
3485
|
<meta charset="UTF-8" />
|
|
3363
3486
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
3364
|
-
<title>${widget2.name} Widget</title
|
|
3487
|
+
<title>${widget2.name} Widget</title>${serverConfig.favicon ? `
|
|
3488
|
+
<link rel="icon" href="/mcp-use/public/${serverConfig.favicon}" />` : ""}
|
|
3365
3489
|
</head>
|
|
3366
3490
|
<body>
|
|
3367
3491
|
<div id="widget-root"></div>
|
|
@@ -3405,6 +3529,50 @@ if (container && Component) {
|
|
|
3405
3529
|
const resourcesPath = pathHelpers.join(getCwd(), resourcesDir);
|
|
3406
3530
|
server.watcher.add(resourcesPath);
|
|
3407
3531
|
console.log(`[WIDGETS] Watching resources directory: ${resourcesPath}`);
|
|
3532
|
+
server.watcher.on("unlink", async (filePath) => {
|
|
3533
|
+
const relativePath = pathHelpers.relative(resourcesPath, filePath);
|
|
3534
|
+
if ((relativePath.endsWith(".tsx") || relativePath.endsWith(".ts")) && !relativePath.includes("/")) {
|
|
3535
|
+
const widgetName = relativePath.replace(/\.tsx?$/, "");
|
|
3536
|
+
const widgetDir = pathHelpers.join(tempDir, widgetName);
|
|
3537
|
+
try {
|
|
3538
|
+
await fs.access(widgetDir);
|
|
3539
|
+
await fs.rm(widgetDir, { recursive: true, force: true });
|
|
3540
|
+
console.log(
|
|
3541
|
+
`[WIDGETS] Cleaned up stale widget (file removed): ${widgetName}`
|
|
3542
|
+
);
|
|
3543
|
+
} catch {
|
|
3544
|
+
}
|
|
3545
|
+
} else if (relativePath.endsWith("widget.tsx")) {
|
|
3546
|
+
const parts = relativePath.split("/");
|
|
3547
|
+
if (parts.length === 2) {
|
|
3548
|
+
const widgetName = parts[0];
|
|
3549
|
+
const widgetDir = pathHelpers.join(tempDir, widgetName);
|
|
3550
|
+
try {
|
|
3551
|
+
await fs.access(widgetDir);
|
|
3552
|
+
await fs.rm(widgetDir, { recursive: true, force: true });
|
|
3553
|
+
console.log(
|
|
3554
|
+
`[WIDGETS] Cleaned up stale widget (file removed): ${widgetName}`
|
|
3555
|
+
);
|
|
3556
|
+
} catch {
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
});
|
|
3561
|
+
server.watcher.on("unlinkDir", async (dirPath) => {
|
|
3562
|
+
const relativePath = pathHelpers.relative(resourcesPath, dirPath);
|
|
3563
|
+
if (relativePath && !relativePath.includes("/")) {
|
|
3564
|
+
const widgetName = relativePath;
|
|
3565
|
+
const widgetDir = pathHelpers.join(tempDir, widgetName);
|
|
3566
|
+
try {
|
|
3567
|
+
await fs.access(widgetDir);
|
|
3568
|
+
await fs.rm(widgetDir, { recursive: true, force: true });
|
|
3569
|
+
console.log(
|
|
3570
|
+
`[WIDGETS] Cleaned up stale widget (directory removed): ${widgetName}`
|
|
3571
|
+
);
|
|
3572
|
+
} catch {
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
});
|
|
3408
3576
|
}
|
|
3409
3577
|
};
|
|
3410
3578
|
const nodeStubsPlugin = {
|
|
@@ -3517,6 +3685,7 @@ export default PostHog;
|
|
|
3517
3685
|
);
|
|
3518
3686
|
app.use(`${baseRoute}/*`, viteMiddleware);
|
|
3519
3687
|
setupPublicRoutes(app, false);
|
|
3688
|
+
setupFaviconRoute(app, serverConfig.favicon, false);
|
|
3520
3689
|
app.use(`${baseRoute}/*`, async (c) => {
|
|
3521
3690
|
const url = new URL(c.req.url);
|
|
3522
3691
|
const isAsset = url.pathname.match(
|
|
@@ -3726,6 +3895,7 @@ function setupWidgetRoutes(app, serverConfig) {
|
|
|
3726
3895
|
}
|
|
3727
3896
|
});
|
|
3728
3897
|
setupPublicRoutes(app, true);
|
|
3898
|
+
setupFaviconRoute(app, serverConfig.favicon, true);
|
|
3729
3899
|
}
|
|
3730
3900
|
__name(setupWidgetRoutes, "setupWidgetRoutes");
|
|
3731
3901
|
|
|
@@ -3860,18 +4030,30 @@ function uiResourceRegistration(server, definition) {
|
|
|
3860
4030
|
);
|
|
3861
4031
|
const uniqueToolMetadata = {
|
|
3862
4032
|
...toolMetadata,
|
|
3863
|
-
"openai/outputTemplate": uniqueUri
|
|
4033
|
+
"openai/outputTemplate": uniqueUri,
|
|
4034
|
+
"mcp-use/props": params
|
|
4035
|
+
// Pass params as widget props
|
|
3864
4036
|
};
|
|
4037
|
+
let toolOutputResult;
|
|
4038
|
+
if (definition.toolOutput) {
|
|
4039
|
+
toolOutputResult = typeof definition.toolOutput === "function" ? definition.toolOutput(params) : definition.toolOutput;
|
|
4040
|
+
} else {
|
|
4041
|
+
toolOutputResult = {
|
|
4042
|
+
content: [
|
|
4043
|
+
{
|
|
4044
|
+
type: "text",
|
|
4045
|
+
text: `Displaying ${displayName}`
|
|
4046
|
+
}
|
|
4047
|
+
]
|
|
4048
|
+
};
|
|
4049
|
+
}
|
|
4050
|
+
const content = toolOutputResult.content || [
|
|
4051
|
+
{ type: "text", text: `Displaying ${displayName}` }
|
|
4052
|
+
];
|
|
3865
4053
|
return {
|
|
3866
4054
|
_meta: uniqueToolMetadata,
|
|
3867
|
-
content
|
|
3868
|
-
|
|
3869
|
-
type: "text",
|
|
3870
|
-
text: `Displaying ${displayName}`
|
|
3871
|
-
}
|
|
3872
|
-
],
|
|
3873
|
-
// structuredContent will be injected as window.openai.toolOutput by Apps SDK
|
|
3874
|
-
structuredContent: params
|
|
4055
|
+
content,
|
|
4056
|
+
structuredContent: toolOutputResult.structuredContent
|
|
3875
4057
|
};
|
|
3876
4058
|
}
|
|
3877
4059
|
return {
|
|
@@ -3897,7 +4079,8 @@ async function mountWidgets(server, options) {
|
|
|
3897
4079
|
serverBaseUrl: server.serverBaseUrl || `http://${server.serverHost}:${server.serverPort || 3e3}`,
|
|
3898
4080
|
serverPort: server.serverPort || 3e3,
|
|
3899
4081
|
cspUrls: getCSPUrls(),
|
|
3900
|
-
buildId: server.buildId
|
|
4082
|
+
buildId: server.buildId,
|
|
4083
|
+
favicon: server.favicon
|
|
3901
4084
|
};
|
|
3902
4085
|
const registerWidget = /* @__PURE__ */ __name((widgetDef) => {
|
|
3903
4086
|
server.uiResource(widgetDef);
|
|
@@ -4392,27 +4575,7 @@ __name(registerPrompt, "registerPrompt");
|
|
|
4392
4575
|
|
|
4393
4576
|
// src/server/roots/roots-registration.ts
|
|
4394
4577
|
init_runtime();
|
|
4395
|
-
|
|
4396
|
-
// src/server/utils/jsonrpc-helpers.ts
|
|
4397
|
-
function createNotification(method, params) {
|
|
4398
|
-
return {
|
|
4399
|
-
jsonrpc: "2.0",
|
|
4400
|
-
method,
|
|
4401
|
-
...params && { params }
|
|
4402
|
-
};
|
|
4403
|
-
}
|
|
4404
|
-
__name(createNotification, "createNotification");
|
|
4405
|
-
function createRequest(id, method, params) {
|
|
4406
|
-
return {
|
|
4407
|
-
jsonrpc: "2.0",
|
|
4408
|
-
id,
|
|
4409
|
-
method,
|
|
4410
|
-
...params && { params }
|
|
4411
|
-
};
|
|
4412
|
-
}
|
|
4413
|
-
__name(createRequest, "createRequest");
|
|
4414
|
-
|
|
4415
|
-
// src/server/roots/roots-registration.ts
|
|
4578
|
+
init_jsonrpc_helpers();
|
|
4416
4579
|
function onRootsChanged(callback) {
|
|
4417
4580
|
this.onRootsChangedCallback = callback;
|
|
4418
4581
|
return this;
|
|
@@ -4574,41 +4737,8 @@ async function requestLogger(c, next) {
|
|
|
4574
4737
|
}
|
|
4575
4738
|
__name(requestLogger, "requestLogger");
|
|
4576
4739
|
|
|
4577
|
-
// src/server/sessions/notifications.ts
|
|
4578
|
-
async function sendNotificationToAll(sessions, method, params) {
|
|
4579
|
-
const notification = createNotification(method, params);
|
|
4580
|
-
for (const [sessionId, session] of sessions.entries()) {
|
|
4581
|
-
try {
|
|
4582
|
-
await session.transport.send(notification);
|
|
4583
|
-
} catch (error2) {
|
|
4584
|
-
console.warn(
|
|
4585
|
-
`[MCP] Failed to send notification to session ${sessionId}:`,
|
|
4586
|
-
error2
|
|
4587
|
-
);
|
|
4588
|
-
}
|
|
4589
|
-
}
|
|
4590
|
-
}
|
|
4591
|
-
__name(sendNotificationToAll, "sendNotificationToAll");
|
|
4592
|
-
async function sendNotificationToSession(sessions, sessionId, method, params) {
|
|
4593
|
-
const session = sessions.get(sessionId);
|
|
4594
|
-
if (!session) {
|
|
4595
|
-
return false;
|
|
4596
|
-
}
|
|
4597
|
-
const notification = createNotification(method, params);
|
|
4598
|
-
try {
|
|
4599
|
-
await session.transport.send(notification);
|
|
4600
|
-
return true;
|
|
4601
|
-
} catch (error2) {
|
|
4602
|
-
console.warn(
|
|
4603
|
-
`[MCP] Failed to send notification to session ${sessionId}:`,
|
|
4604
|
-
error2
|
|
4605
|
-
);
|
|
4606
|
-
return false;
|
|
4607
|
-
}
|
|
4608
|
-
}
|
|
4609
|
-
__name(sendNotificationToSession, "sendNotificationToSession");
|
|
4610
|
-
|
|
4611
4740
|
// src/server/notifications/notification-registration.ts
|
|
4741
|
+
init_notifications();
|
|
4612
4742
|
function getActiveSessions() {
|
|
4613
4743
|
return Array.from(this.sessions.keys());
|
|
4614
4744
|
}
|
|
@@ -4626,13 +4756,34 @@ async function sendNotificationToSession2(sessionId, method, params) {
|
|
|
4626
4756
|
);
|
|
4627
4757
|
}
|
|
4628
4758
|
__name(sendNotificationToSession2, "sendNotificationToSession");
|
|
4759
|
+
async function sendToolsListChanged() {
|
|
4760
|
+
await sendNotificationToAll(
|
|
4761
|
+
this.sessions,
|
|
4762
|
+
"notifications/tools/list_changed"
|
|
4763
|
+
);
|
|
4764
|
+
}
|
|
4765
|
+
__name(sendToolsListChanged, "sendToolsListChanged");
|
|
4766
|
+
async function sendResourcesListChanged() {
|
|
4767
|
+
await sendNotificationToAll(
|
|
4768
|
+
this.sessions,
|
|
4769
|
+
"notifications/resources/list_changed"
|
|
4770
|
+
);
|
|
4771
|
+
}
|
|
4772
|
+
__name(sendResourcesListChanged, "sendResourcesListChanged");
|
|
4773
|
+
async function sendPromptsListChanged() {
|
|
4774
|
+
await sendNotificationToAll(
|
|
4775
|
+
this.sessions,
|
|
4776
|
+
"notifications/prompts/list_changed"
|
|
4777
|
+
);
|
|
4778
|
+
}
|
|
4779
|
+
__name(sendPromptsListChanged, "sendPromptsListChanged");
|
|
4629
4780
|
|
|
4630
4781
|
// src/server/mcp-server.ts
|
|
4631
4782
|
init_tool_execution_helpers();
|
|
4632
4783
|
init_context_storage();
|
|
4633
4784
|
|
|
4634
4785
|
// src/server/sessions/session-manager.ts
|
|
4635
|
-
function startIdleCleanup(sessions, idleTimeoutMs, mcpServerInstance) {
|
|
4786
|
+
function startIdleCleanup(sessions, idleTimeoutMs, transports, mcpServerInstance) {
|
|
4636
4787
|
if (idleTimeoutMs <= 0) {
|
|
4637
4788
|
return void 0;
|
|
4638
4789
|
}
|
|
@@ -4649,82 +4800,1068 @@ function startIdleCleanup(sessions, idleTimeoutMs, mcpServerInstance) {
|
|
|
4649
4800
|
`[MCP] Cleaning up ${expiredSessions.length} idle session(s)`
|
|
4650
4801
|
);
|
|
4651
4802
|
for (const sessionId of expiredSessions) {
|
|
4803
|
+
const transport = transports?.get(sessionId);
|
|
4804
|
+
if (transport?.close) {
|
|
4805
|
+
Promise.resolve(transport.close()).catch((e) => {
|
|
4806
|
+
console.warn(
|
|
4807
|
+
`[MCP] Error closing transport for session ${sessionId}:`,
|
|
4808
|
+
e
|
|
4809
|
+
);
|
|
4810
|
+
});
|
|
4811
|
+
}
|
|
4812
|
+
transports?.delete(sessionId);
|
|
4652
4813
|
sessions.delete(sessionId);
|
|
4653
4814
|
mcpServerInstance?.cleanupSessionSubscriptions?.(sessionId);
|
|
4815
|
+
console.log(
|
|
4816
|
+
`[MCP] Cleaned up resource subscriptions for session ${sessionId}`
|
|
4817
|
+
);
|
|
4654
4818
|
}
|
|
4655
4819
|
}
|
|
4656
4820
|
}, 6e4);
|
|
4657
4821
|
}
|
|
4658
4822
|
__name(startIdleCleanup, "startIdleCleanup");
|
|
4659
4823
|
|
|
4660
|
-
// src/server/
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
let idleCleanupInterval;
|
|
4668
|
-
if (!config.stateless && idleTimeoutMs > 0) {
|
|
4669
|
-
idleCleanupInterval = startIdleCleanup(
|
|
4670
|
-
sessions,
|
|
4671
|
-
idleTimeoutMs,
|
|
4672
|
-
mcpServerInstance
|
|
4673
|
-
);
|
|
4824
|
+
// src/server/sessions/index.ts
|
|
4825
|
+
init_notifications();
|
|
4826
|
+
|
|
4827
|
+
// src/server/sessions/stores/memory.ts
|
|
4828
|
+
var InMemorySessionStore = class {
|
|
4829
|
+
static {
|
|
4830
|
+
__name(this, "InMemorySessionStore");
|
|
4674
4831
|
}
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4832
|
+
/**
|
|
4833
|
+
* Internal map storing session metadata
|
|
4834
|
+
* Key: sessionId, Value: SessionMetadata
|
|
4835
|
+
*/
|
|
4836
|
+
sessions = /* @__PURE__ */ new Map();
|
|
4837
|
+
/**
|
|
4838
|
+
* Retrieve session metadata by ID
|
|
4839
|
+
*/
|
|
4840
|
+
async get(sessionId) {
|
|
4841
|
+
const data = this.sessions.get(sessionId);
|
|
4842
|
+
return data ?? null;
|
|
4843
|
+
}
|
|
4844
|
+
/**
|
|
4845
|
+
* Store or update session metadata
|
|
4846
|
+
*/
|
|
4847
|
+
async set(sessionId, data) {
|
|
4848
|
+
this.sessions.set(sessionId, data);
|
|
4849
|
+
}
|
|
4850
|
+
/**
|
|
4851
|
+
* Delete session metadata
|
|
4852
|
+
*/
|
|
4853
|
+
async delete(sessionId) {
|
|
4854
|
+
this.sessions.delete(sessionId);
|
|
4855
|
+
}
|
|
4856
|
+
/**
|
|
4857
|
+
* Check if session exists
|
|
4858
|
+
*/
|
|
4859
|
+
async has(sessionId) {
|
|
4860
|
+
return this.sessions.has(sessionId);
|
|
4861
|
+
}
|
|
4862
|
+
/**
|
|
4863
|
+
* List all session IDs
|
|
4864
|
+
*/
|
|
4865
|
+
async keys() {
|
|
4866
|
+
return Array.from(this.sessions.keys());
|
|
4867
|
+
}
|
|
4868
|
+
/**
|
|
4869
|
+
* Store session metadata with TTL (time-to-live)
|
|
4870
|
+
*
|
|
4871
|
+
* Note: In-memory implementation uses setTimeout for TTL.
|
|
4872
|
+
* For production TTL support, use Redis or another store with native TTL.
|
|
4873
|
+
*/
|
|
4874
|
+
async setWithTTL(sessionId, data, ttlMs) {
|
|
4875
|
+
this.sessions.set(sessionId, data);
|
|
4876
|
+
setTimeout(() => {
|
|
4877
|
+
this.sessions.delete(sessionId);
|
|
4878
|
+
console.log(`[MCP] Session ${sessionId} expired after ${ttlMs}ms`);
|
|
4879
|
+
}, ttlMs);
|
|
4880
|
+
}
|
|
4881
|
+
/**
|
|
4882
|
+
* Get the number of active sessions
|
|
4883
|
+
* Useful for monitoring and debugging
|
|
4884
|
+
*/
|
|
4885
|
+
get size() {
|
|
4886
|
+
return this.sessions.size;
|
|
4887
|
+
}
|
|
4888
|
+
/**
|
|
4889
|
+
* Clear all sessions
|
|
4890
|
+
* Useful for testing and manual cleanup
|
|
4891
|
+
*/
|
|
4892
|
+
async clear() {
|
|
4893
|
+
this.sessions.clear();
|
|
4894
|
+
}
|
|
4895
|
+
};
|
|
4896
|
+
|
|
4897
|
+
// src/server/sessions/stores/redis.ts
|
|
4898
|
+
var RedisSessionStore = class {
|
|
4899
|
+
static {
|
|
4900
|
+
__name(this, "RedisSessionStore");
|
|
4901
|
+
}
|
|
4902
|
+
client;
|
|
4903
|
+
prefix;
|
|
4904
|
+
defaultTTL;
|
|
4905
|
+
constructor(config) {
|
|
4906
|
+
this.client = config.client;
|
|
4907
|
+
this.prefix = config.prefix ?? "mcp:session:";
|
|
4908
|
+
this.defaultTTL = config.defaultTTL ?? 3600;
|
|
4909
|
+
}
|
|
4910
|
+
/**
|
|
4911
|
+
* Get full Redis key for a session ID
|
|
4912
|
+
*/
|
|
4913
|
+
getKey(sessionId) {
|
|
4914
|
+
return `${this.prefix}${sessionId}`;
|
|
4915
|
+
}
|
|
4916
|
+
/**
|
|
4917
|
+
* Retrieve session metadata by ID
|
|
4918
|
+
*/
|
|
4919
|
+
async get(sessionId) {
|
|
4920
|
+
try {
|
|
4921
|
+
const key = this.getKey(sessionId);
|
|
4922
|
+
const data = await this.client.get(key);
|
|
4923
|
+
if (!data) {
|
|
4924
|
+
return null;
|
|
4702
4925
|
}
|
|
4703
|
-
|
|
4926
|
+
return JSON.parse(data);
|
|
4927
|
+
} catch (error2) {
|
|
4928
|
+
console.error(
|
|
4929
|
+
`[RedisSessionStore] Error getting session ${sessionId}:`,
|
|
4930
|
+
error2
|
|
4931
|
+
);
|
|
4932
|
+
return null;
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
/**
|
|
4936
|
+
* Store or update session metadata
|
|
4937
|
+
*/
|
|
4938
|
+
async set(sessionId, data) {
|
|
4939
|
+
try {
|
|
4940
|
+
const key = this.getKey(sessionId);
|
|
4941
|
+
const value = JSON.stringify(data);
|
|
4942
|
+
if (this.client.setEx) {
|
|
4943
|
+
await this.client.setEx(key, this.defaultTTL, value);
|
|
4944
|
+
} else if (this.client.setex) {
|
|
4945
|
+
await this.client.setex(key, this.defaultTTL, value);
|
|
4946
|
+
} else {
|
|
4947
|
+
await this.client.set(key, value, { EX: this.defaultTTL });
|
|
4948
|
+
}
|
|
4949
|
+
} catch (error2) {
|
|
4950
|
+
console.error(
|
|
4951
|
+
`[RedisSessionStore] Error setting session ${sessionId}:`,
|
|
4952
|
+
error2
|
|
4953
|
+
);
|
|
4954
|
+
throw error2;
|
|
4955
|
+
}
|
|
4956
|
+
}
|
|
4957
|
+
/**
|
|
4958
|
+
* Delete a session
|
|
4959
|
+
*/
|
|
4960
|
+
async delete(sessionId) {
|
|
4961
|
+
try {
|
|
4962
|
+
const key = this.getKey(sessionId);
|
|
4963
|
+
await this.client.del(key);
|
|
4964
|
+
} catch (error2) {
|
|
4965
|
+
console.error(
|
|
4966
|
+
`[RedisSessionStore] Error deleting session ${sessionId}:`,
|
|
4967
|
+
error2
|
|
4968
|
+
);
|
|
4969
|
+
throw error2;
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
/**
|
|
4973
|
+
* Check if a session exists
|
|
4974
|
+
*/
|
|
4975
|
+
async has(sessionId) {
|
|
4976
|
+
try {
|
|
4977
|
+
const key = this.getKey(sessionId);
|
|
4978
|
+
const exists = await this.client.exists(key);
|
|
4979
|
+
return exists === 1;
|
|
4980
|
+
} catch (error2) {
|
|
4981
|
+
console.error(
|
|
4982
|
+
`[RedisSessionStore] Error checking session ${sessionId}:`,
|
|
4983
|
+
error2
|
|
4984
|
+
);
|
|
4985
|
+
return false;
|
|
4986
|
+
}
|
|
4987
|
+
}
|
|
4988
|
+
/**
|
|
4989
|
+
* List all session IDs
|
|
4990
|
+
*
|
|
4991
|
+
* WARNING: Uses KEYS command which blocks Redis. For production systems with
|
|
4992
|
+
* many sessions, consider using SCAN instead or maintaining a separate SET of
|
|
4993
|
+
* active session IDs.
|
|
4994
|
+
*/
|
|
4995
|
+
async keys() {
|
|
4996
|
+
try {
|
|
4997
|
+
const pattern = `${this.prefix}*`;
|
|
4998
|
+
const keys = await this.client.keys(pattern);
|
|
4999
|
+
return keys.map((key) => key.substring(this.prefix.length));
|
|
5000
|
+
} catch (error2) {
|
|
5001
|
+
console.error("[RedisSessionStore] Error listing session keys:", error2);
|
|
5002
|
+
return [];
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
/**
|
|
5006
|
+
* Store session metadata with custom TTL (time-to-live)
|
|
5007
|
+
*/
|
|
5008
|
+
async setWithTTL(sessionId, data, ttlMs) {
|
|
5009
|
+
try {
|
|
5010
|
+
const key = this.getKey(sessionId);
|
|
5011
|
+
const value = JSON.stringify(data);
|
|
5012
|
+
const ttlSeconds = Math.ceil(ttlMs / 1e3);
|
|
5013
|
+
if (this.client.setEx) {
|
|
5014
|
+
await this.client.setEx(key, ttlSeconds, value);
|
|
5015
|
+
} else if (this.client.setex) {
|
|
5016
|
+
await this.client.setex(key, ttlSeconds, value);
|
|
5017
|
+
} else {
|
|
5018
|
+
await this.client.set(key, value, { EX: ttlSeconds });
|
|
5019
|
+
}
|
|
5020
|
+
} catch (error2) {
|
|
5021
|
+
console.error(
|
|
5022
|
+
`[RedisSessionStore] Error setting session ${sessionId} with TTL:`,
|
|
5023
|
+
error2
|
|
5024
|
+
);
|
|
5025
|
+
throw error2;
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
/**
|
|
5029
|
+
* Close Redis connection
|
|
5030
|
+
* Should be called when shutting down the server
|
|
5031
|
+
*/
|
|
5032
|
+
async close() {
|
|
5033
|
+
try {
|
|
5034
|
+
await this.client.quit();
|
|
5035
|
+
} catch (error2) {
|
|
5036
|
+
console.error(
|
|
5037
|
+
"[RedisSessionStore] Error closing Redis connection:",
|
|
5038
|
+
error2
|
|
5039
|
+
);
|
|
5040
|
+
throw error2;
|
|
5041
|
+
}
|
|
5042
|
+
}
|
|
5043
|
+
/**
|
|
5044
|
+
* Clear all sessions (useful for testing)
|
|
5045
|
+
* WARNING: This will delete all sessions with the configured prefix
|
|
5046
|
+
*
|
|
5047
|
+
* NOTE: Uses KEYS command which blocks Redis. This is acceptable for testing
|
|
5048
|
+
* but should be avoided in production with large datasets.
|
|
5049
|
+
*/
|
|
5050
|
+
async clear() {
|
|
5051
|
+
try {
|
|
5052
|
+
const pattern = `${this.prefix}*`;
|
|
5053
|
+
const keys = await this.client.keys(pattern);
|
|
5054
|
+
if (keys.length > 0) {
|
|
5055
|
+
await this.client.del(keys);
|
|
5056
|
+
}
|
|
5057
|
+
} catch (error2) {
|
|
5058
|
+
console.error("[RedisSessionStore] Error clearing sessions:", error2);
|
|
5059
|
+
throw error2;
|
|
5060
|
+
}
|
|
5061
|
+
}
|
|
5062
|
+
};
|
|
5063
|
+
|
|
5064
|
+
// src/server/sessions/stores/filesystem.ts
|
|
5065
|
+
var import_promises = require("fs/promises");
|
|
5066
|
+
var import_node_path = require("path");
|
|
5067
|
+
var import_node_fs = require("fs");
|
|
5068
|
+
var FileSystemSessionStore = class {
|
|
5069
|
+
static {
|
|
5070
|
+
__name(this, "FileSystemSessionStore");
|
|
5071
|
+
}
|
|
5072
|
+
sessions = /* @__PURE__ */ new Map();
|
|
5073
|
+
filePath;
|
|
5074
|
+
debounceMs;
|
|
5075
|
+
maxAgeMs;
|
|
5076
|
+
saveTimer = null;
|
|
5077
|
+
saving = false;
|
|
5078
|
+
pendingSave = false;
|
|
5079
|
+
constructor(config = {}) {
|
|
5080
|
+
this.filePath = config.path ?? (0, import_node_path.join)(process.cwd(), ".mcp-use", "sessions.json");
|
|
5081
|
+
this.debounceMs = config.debounceMs ?? 100;
|
|
5082
|
+
this.maxAgeMs = config.maxAgeMs ?? 24 * 60 * 60 * 1e3;
|
|
5083
|
+
this.loadSessionsSync();
|
|
5084
|
+
}
|
|
5085
|
+
/**
|
|
5086
|
+
* Load sessions from file synchronously during construction
|
|
5087
|
+
* This ensures sessions are available immediately when the server starts
|
|
5088
|
+
*/
|
|
5089
|
+
loadSessionsSync() {
|
|
5090
|
+
try {
|
|
5091
|
+
if (!(0, import_node_fs.existsSync)(this.filePath)) {
|
|
5092
|
+
console.log(
|
|
5093
|
+
`[FileSystemSessionStore] No session file found at ${this.filePath}, starting fresh`
|
|
5094
|
+
);
|
|
5095
|
+
return;
|
|
5096
|
+
}
|
|
5097
|
+
const data = (0, import_node_fs.readFileSync)(this.filePath, "utf-8");
|
|
5098
|
+
const parsed = JSON.parse(data);
|
|
5099
|
+
const now = Date.now();
|
|
5100
|
+
let loadedCount = 0;
|
|
5101
|
+
let expiredCount = 0;
|
|
5102
|
+
for (const [sessionId, metadata] of Object.entries(parsed)) {
|
|
5103
|
+
const sessionMetadata = metadata;
|
|
5104
|
+
const age = now - sessionMetadata.lastAccessedAt;
|
|
5105
|
+
if (age > this.maxAgeMs) {
|
|
5106
|
+
expiredCount++;
|
|
5107
|
+
continue;
|
|
5108
|
+
}
|
|
5109
|
+
this.sessions.set(sessionId, sessionMetadata);
|
|
5110
|
+
loadedCount++;
|
|
5111
|
+
}
|
|
5112
|
+
console.log(
|
|
5113
|
+
`[FileSystemSessionStore] Loaded ${loadedCount} session(s) from ${this.filePath}` + (expiredCount > 0 ? ` (cleaned up ${expiredCount} expired)` : "")
|
|
5114
|
+
);
|
|
5115
|
+
} catch (error2) {
|
|
5116
|
+
if (error2.code === "ENOENT") {
|
|
5117
|
+
console.log(`[FileSystemSessionStore] No existing sessions file`);
|
|
5118
|
+
} else if (error2 instanceof SyntaxError) {
|
|
5119
|
+
console.warn(
|
|
5120
|
+
`[FileSystemSessionStore] Corrupted session file, starting fresh:`,
|
|
5121
|
+
error2.message
|
|
5122
|
+
);
|
|
5123
|
+
} else {
|
|
5124
|
+
console.warn(
|
|
5125
|
+
`[FileSystemSessionStore] Error loading sessions, starting fresh:`,
|
|
5126
|
+
error2.message
|
|
5127
|
+
);
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
/**
|
|
5132
|
+
* Retrieve session metadata by ID
|
|
5133
|
+
*/
|
|
5134
|
+
async get(sessionId) {
|
|
5135
|
+
const data = this.sessions.get(sessionId);
|
|
5136
|
+
return data ?? null;
|
|
5137
|
+
}
|
|
5138
|
+
/**
|
|
5139
|
+
* Store or update session metadata
|
|
5140
|
+
* Uses debouncing to batch rapid consecutive writes
|
|
5141
|
+
*/
|
|
5142
|
+
async set(sessionId, data) {
|
|
5143
|
+
this.sessions.set(sessionId, data);
|
|
5144
|
+
await this.scheduleSave();
|
|
5145
|
+
}
|
|
5146
|
+
/**
|
|
5147
|
+
* Delete session metadata
|
|
5148
|
+
*/
|
|
5149
|
+
async delete(sessionId) {
|
|
5150
|
+
this.sessions.delete(sessionId);
|
|
5151
|
+
await this.scheduleSave();
|
|
5152
|
+
}
|
|
5153
|
+
/**
|
|
5154
|
+
* Check if session exists
|
|
5155
|
+
*/
|
|
5156
|
+
async has(sessionId) {
|
|
5157
|
+
return this.sessions.has(sessionId);
|
|
5158
|
+
}
|
|
5159
|
+
/**
|
|
5160
|
+
* List all session IDs
|
|
5161
|
+
*/
|
|
5162
|
+
async keys() {
|
|
5163
|
+
return Array.from(this.sessions.keys());
|
|
5164
|
+
}
|
|
5165
|
+
/**
|
|
5166
|
+
* Store session metadata with TTL
|
|
5167
|
+
* Note: TTL is enforced on load, not with timers (simple implementation)
|
|
5168
|
+
*/
|
|
5169
|
+
async setWithTTL(sessionId, data, ttlMs) {
|
|
5170
|
+
const metadataWithExpiry = {
|
|
5171
|
+
...data,
|
|
5172
|
+
lastAccessedAt: Date.now()
|
|
5173
|
+
};
|
|
5174
|
+
this.sessions.set(sessionId, metadataWithExpiry);
|
|
5175
|
+
await this.scheduleSave();
|
|
5176
|
+
setTimeout(() => {
|
|
5177
|
+
this.sessions.delete(sessionId);
|
|
5178
|
+
this.scheduleSave();
|
|
5179
|
+
}, ttlMs);
|
|
5180
|
+
}
|
|
5181
|
+
/**
|
|
5182
|
+
* Get the number of active sessions
|
|
5183
|
+
*/
|
|
5184
|
+
get size() {
|
|
5185
|
+
return this.sessions.size;
|
|
5186
|
+
}
|
|
5187
|
+
/**
|
|
5188
|
+
* Clear all sessions
|
|
5189
|
+
*/
|
|
5190
|
+
async clear() {
|
|
5191
|
+
this.sessions.clear();
|
|
5192
|
+
await this.scheduleSave();
|
|
5193
|
+
}
|
|
5194
|
+
/**
|
|
5195
|
+
* Schedule a save operation with debouncing
|
|
5196
|
+
* Prevents excessive disk I/O from rapid consecutive writes
|
|
5197
|
+
*/
|
|
5198
|
+
async scheduleSave() {
|
|
5199
|
+
if (this.saving) {
|
|
5200
|
+
this.pendingSave = true;
|
|
5201
|
+
return;
|
|
5202
|
+
}
|
|
5203
|
+
if (this.saveTimer) {
|
|
5204
|
+
clearTimeout(this.saveTimer);
|
|
5205
|
+
}
|
|
5206
|
+
this.saveTimer = setTimeout(() => {
|
|
5207
|
+
this.performSave();
|
|
5208
|
+
}, this.debounceMs);
|
|
5209
|
+
}
|
|
5210
|
+
/**
|
|
5211
|
+
* Perform the actual save operation with atomic writes
|
|
5212
|
+
* Uses write-to-temp-then-rename pattern to prevent corruption
|
|
5213
|
+
*/
|
|
5214
|
+
async performSave() {
|
|
5215
|
+
this.saveTimer = null;
|
|
5216
|
+
this.saving = true;
|
|
5217
|
+
this.pendingSave = false;
|
|
5218
|
+
try {
|
|
5219
|
+
const dir = (0, import_node_path.dirname)(this.filePath);
|
|
5220
|
+
await (0, import_promises.mkdir)(dir, { recursive: true });
|
|
5221
|
+
const data = {};
|
|
5222
|
+
for (const [sessionId, metadata] of Array.from(this.sessions.entries())) {
|
|
5223
|
+
data[sessionId] = metadata;
|
|
5224
|
+
}
|
|
5225
|
+
const tempPath = `${this.filePath}.tmp`;
|
|
5226
|
+
await (0, import_promises.writeFile)(tempPath, JSON.stringify(data, null, 2), "utf-8");
|
|
5227
|
+
await (0, import_promises.rename)(tempPath, this.filePath);
|
|
5228
|
+
console.debug(
|
|
5229
|
+
`[FileSystemSessionStore] Saved ${this.sessions.size} session(s) to ${this.filePath}`
|
|
5230
|
+
);
|
|
5231
|
+
} catch (error2) {
|
|
5232
|
+
console.error(
|
|
5233
|
+
`[FileSystemSessionStore] Error saving sessions:`,
|
|
5234
|
+
error2.message
|
|
5235
|
+
);
|
|
5236
|
+
try {
|
|
5237
|
+
const tempPath = `${this.filePath}.tmp`;
|
|
5238
|
+
if ((0, import_node_fs.existsSync)(tempPath)) {
|
|
5239
|
+
await (0, import_promises.unlink)(tempPath);
|
|
5240
|
+
}
|
|
5241
|
+
} catch {
|
|
5242
|
+
}
|
|
5243
|
+
} finally {
|
|
5244
|
+
this.saving = false;
|
|
5245
|
+
if (this.pendingSave) {
|
|
5246
|
+
await this.scheduleSave();
|
|
5247
|
+
}
|
|
5248
|
+
}
|
|
5249
|
+
}
|
|
5250
|
+
/**
|
|
5251
|
+
* Force an immediate save (bypasses debouncing)
|
|
5252
|
+
* Useful for ensuring persistence before process exit
|
|
5253
|
+
*/
|
|
5254
|
+
async flush() {
|
|
5255
|
+
if (this.saveTimer) {
|
|
5256
|
+
clearTimeout(this.saveTimer);
|
|
5257
|
+
this.saveTimer = null;
|
|
5258
|
+
}
|
|
5259
|
+
await this.performSave();
|
|
5260
|
+
}
|
|
5261
|
+
};
|
|
5262
|
+
|
|
5263
|
+
// src/server/sessions/streams/memory.ts
|
|
5264
|
+
var InMemoryStreamManager = class {
|
|
5265
|
+
static {
|
|
5266
|
+
__name(this, "InMemoryStreamManager");
|
|
5267
|
+
}
|
|
5268
|
+
/**
|
|
5269
|
+
* Map of active SSE stream controllers
|
|
5270
|
+
* Key: sessionId, Value: ReadableStreamDefaultController
|
|
5271
|
+
*/
|
|
5272
|
+
streams = /* @__PURE__ */ new Map();
|
|
5273
|
+
/**
|
|
5274
|
+
* Text encoder for converting strings to Uint8Array
|
|
5275
|
+
*/
|
|
5276
|
+
textEncoder = new TextEncoder();
|
|
5277
|
+
/**
|
|
5278
|
+
* Register an active SSE stream controller
|
|
5279
|
+
*/
|
|
5280
|
+
async create(sessionId, controller) {
|
|
5281
|
+
this.streams.set(sessionId, controller);
|
|
5282
|
+
}
|
|
5283
|
+
/**
|
|
5284
|
+
* Send data to active SSE streams
|
|
5285
|
+
*
|
|
5286
|
+
* Directly enqueues data to in-memory controllers.
|
|
5287
|
+
* For distributed deployments, use RedisStreamManager instead.
|
|
5288
|
+
*/
|
|
5289
|
+
async send(sessionIds, data) {
|
|
5290
|
+
const encoded = this.textEncoder.encode(data);
|
|
5291
|
+
if (!sessionIds) {
|
|
5292
|
+
for (const [_id, controller] of this.streams.entries()) {
|
|
5293
|
+
try {
|
|
5294
|
+
controller.enqueue(encoded);
|
|
5295
|
+
} catch (error2) {
|
|
5296
|
+
console.warn(
|
|
5297
|
+
`[InMemoryStreamManager] Failed to send to session ${_id}:`,
|
|
5298
|
+
error2
|
|
5299
|
+
);
|
|
5300
|
+
}
|
|
5301
|
+
}
|
|
5302
|
+
} else {
|
|
5303
|
+
for (const sessionId of sessionIds) {
|
|
5304
|
+
const controller = this.streams.get(sessionId);
|
|
5305
|
+
if (controller) {
|
|
5306
|
+
try {
|
|
5307
|
+
controller.enqueue(encoded);
|
|
5308
|
+
} catch (error2) {
|
|
5309
|
+
console.warn(
|
|
5310
|
+
`[InMemoryStreamManager] Failed to send to session ${sessionId}:`,
|
|
5311
|
+
error2
|
|
5312
|
+
);
|
|
5313
|
+
}
|
|
5314
|
+
}
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
5317
|
+
}
|
|
5318
|
+
/**
|
|
5319
|
+
* Remove an active SSE stream
|
|
5320
|
+
*/
|
|
5321
|
+
async delete(sessionId) {
|
|
5322
|
+
const controller = this.streams.get(sessionId);
|
|
5323
|
+
if (controller) {
|
|
5324
|
+
try {
|
|
5325
|
+
controller.close();
|
|
5326
|
+
} catch (error2) {
|
|
5327
|
+
console.debug(
|
|
5328
|
+
`[InMemoryStreamManager] Controller already closed for session ${sessionId}`
|
|
5329
|
+
);
|
|
5330
|
+
}
|
|
5331
|
+
this.streams.delete(sessionId);
|
|
5332
|
+
}
|
|
5333
|
+
}
|
|
5334
|
+
/**
|
|
5335
|
+
* Check if an active stream exists
|
|
5336
|
+
*/
|
|
5337
|
+
async has(sessionId) {
|
|
5338
|
+
return this.streams.has(sessionId);
|
|
5339
|
+
}
|
|
5340
|
+
/**
|
|
5341
|
+
* Close all active streams
|
|
5342
|
+
*/
|
|
5343
|
+
async close() {
|
|
5344
|
+
for (const [sessionId, controller] of this.streams.entries()) {
|
|
5345
|
+
try {
|
|
5346
|
+
controller.close();
|
|
5347
|
+
} catch (error2) {
|
|
5348
|
+
console.debug(
|
|
5349
|
+
`[InMemoryStreamManager] Error closing stream for ${sessionId}:`,
|
|
5350
|
+
error2
|
|
5351
|
+
);
|
|
5352
|
+
}
|
|
5353
|
+
}
|
|
5354
|
+
this.streams.clear();
|
|
5355
|
+
}
|
|
5356
|
+
/**
|
|
5357
|
+
* Get the number of active streams
|
|
5358
|
+
* Useful for monitoring
|
|
5359
|
+
*/
|
|
5360
|
+
get size() {
|
|
5361
|
+
return this.streams.size;
|
|
5362
|
+
}
|
|
5363
|
+
};
|
|
5364
|
+
|
|
5365
|
+
// src/server/sessions/streams/redis.ts
|
|
5366
|
+
var RedisStreamManager = class {
|
|
5367
|
+
static {
|
|
5368
|
+
__name(this, "RedisStreamManager");
|
|
5369
|
+
}
|
|
5370
|
+
pubSubClient;
|
|
5371
|
+
client;
|
|
5372
|
+
prefix;
|
|
5373
|
+
heartbeatInterval;
|
|
5374
|
+
textEncoder = new TextEncoder();
|
|
5375
|
+
/**
|
|
5376
|
+
* Map of local controllers (only on this server instance)
|
|
5377
|
+
* Key: sessionId, Value: controller
|
|
5378
|
+
*/
|
|
5379
|
+
localControllers = /* @__PURE__ */ new Map();
|
|
5380
|
+
/**
|
|
5381
|
+
* Map of heartbeat intervals for keeping sessions alive
|
|
5382
|
+
* Key: sessionId, Value: interval timer
|
|
5383
|
+
*/
|
|
5384
|
+
heartbeats = /* @__PURE__ */ new Map();
|
|
5385
|
+
constructor(config) {
|
|
5386
|
+
this.pubSubClient = config.pubSubClient;
|
|
5387
|
+
this.client = config.client;
|
|
5388
|
+
this.prefix = config.prefix ?? "mcp:stream:";
|
|
5389
|
+
this.heartbeatInterval = config.heartbeatInterval ?? 10;
|
|
5390
|
+
}
|
|
5391
|
+
/**
|
|
5392
|
+
* Get the Redis channel name for a session
|
|
5393
|
+
*/
|
|
5394
|
+
getChannel(sessionId) {
|
|
5395
|
+
return `${this.prefix}${sessionId}`;
|
|
5396
|
+
}
|
|
5397
|
+
/**
|
|
5398
|
+
* Get the Redis key for tracking active sessions
|
|
5399
|
+
*/
|
|
5400
|
+
getAvailableKey(sessionId) {
|
|
5401
|
+
return `available:${this.prefix}${sessionId}`;
|
|
5402
|
+
}
|
|
5403
|
+
/**
|
|
5404
|
+
* Get the Redis key for the active sessions SET
|
|
5405
|
+
*/
|
|
5406
|
+
getActiveSessionsKey() {
|
|
5407
|
+
return `${this.prefix}active`;
|
|
5408
|
+
}
|
|
5409
|
+
/**
|
|
5410
|
+
* Register an active SSE stream and subscribe to Redis channel
|
|
5411
|
+
*/
|
|
5412
|
+
async create(sessionId, controller) {
|
|
5413
|
+
try {
|
|
5414
|
+
this.localControllers.set(sessionId, controller);
|
|
5415
|
+
const availableKey = this.getAvailableKey(sessionId);
|
|
5416
|
+
await this.client.set(availableKey, "active");
|
|
5417
|
+
if (this.client.expire) {
|
|
5418
|
+
await this.client.expire(availableKey, this.heartbeatInterval * 2);
|
|
5419
|
+
}
|
|
5420
|
+
const activeSessionsKey = this.getActiveSessionsKey();
|
|
5421
|
+
if (this.client.sAdd) {
|
|
5422
|
+
await this.client.sAdd(activeSessionsKey, sessionId);
|
|
5423
|
+
if (this.client.expire) {
|
|
5424
|
+
await this.client.expire(
|
|
5425
|
+
activeSessionsKey,
|
|
5426
|
+
this.heartbeatInterval * 2
|
|
5427
|
+
);
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
const heartbeat = setInterval(async () => {
|
|
5431
|
+
try {
|
|
5432
|
+
if (this.client.expire) {
|
|
5433
|
+
await this.client.expire(availableKey, this.heartbeatInterval * 2);
|
|
5434
|
+
const activeSessionsKey2 = this.getActiveSessionsKey();
|
|
5435
|
+
await this.client.expire(
|
|
5436
|
+
activeSessionsKey2,
|
|
5437
|
+
this.heartbeatInterval * 2
|
|
5438
|
+
);
|
|
5439
|
+
}
|
|
5440
|
+
} catch (error2) {
|
|
5441
|
+
console.warn(
|
|
5442
|
+
`[RedisStreamManager] Heartbeat failed for session ${sessionId}:`,
|
|
5443
|
+
error2
|
|
5444
|
+
);
|
|
5445
|
+
}
|
|
5446
|
+
}, this.heartbeatInterval * 1e3);
|
|
5447
|
+
this.heartbeats.set(sessionId, heartbeat);
|
|
5448
|
+
const channel = this.getChannel(sessionId);
|
|
5449
|
+
if (!this.pubSubClient.subscribe) {
|
|
5450
|
+
throw new Error(
|
|
5451
|
+
"[RedisStreamManager] Redis client does not support subscribe method"
|
|
5452
|
+
);
|
|
5453
|
+
}
|
|
5454
|
+
await this.pubSubClient.subscribe(channel, (message) => {
|
|
5455
|
+
const localController = this.localControllers.get(sessionId);
|
|
5456
|
+
if (localController) {
|
|
5457
|
+
try {
|
|
5458
|
+
localController.enqueue(this.textEncoder.encode(message));
|
|
5459
|
+
} catch (error2) {
|
|
5460
|
+
console.warn(
|
|
5461
|
+
`[RedisStreamManager] Failed to enqueue message for ${sessionId}:`,
|
|
5462
|
+
error2
|
|
5463
|
+
);
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5466
|
+
});
|
|
5467
|
+
const deleteChannel = `delete:${this.getChannel(sessionId)}`;
|
|
5468
|
+
await this.pubSubClient.subscribe(deleteChannel, async () => {
|
|
5469
|
+
await this.delete(sessionId);
|
|
5470
|
+
});
|
|
5471
|
+
console.log(
|
|
5472
|
+
`[RedisStreamManager] Created stream for session ${sessionId}`
|
|
5473
|
+
);
|
|
5474
|
+
} catch (error2) {
|
|
5475
|
+
console.error(
|
|
5476
|
+
`[RedisStreamManager] Error creating stream for ${sessionId}:`,
|
|
5477
|
+
error2
|
|
5478
|
+
);
|
|
5479
|
+
throw error2;
|
|
5480
|
+
}
|
|
5481
|
+
}
|
|
5482
|
+
/**
|
|
5483
|
+
* Send data to sessions via Redis Pub/Sub
|
|
5484
|
+
*
|
|
5485
|
+
* This works across distributed servers - any server with an active
|
|
5486
|
+
* SSE connection for the target session will receive and forward the message.
|
|
5487
|
+
*
|
|
5488
|
+
* Note: Uses the regular client (not pubSubClient) for publishing.
|
|
5489
|
+
* In node-redis v5+, clients in subscriber mode cannot publish.
|
|
5490
|
+
*/
|
|
5491
|
+
async send(sessionIds, data) {
|
|
5492
|
+
try {
|
|
5493
|
+
if (!sessionIds) {
|
|
5494
|
+
const activeSessionsKey = this.getActiveSessionsKey();
|
|
5495
|
+
if (this.client.sMembers) {
|
|
5496
|
+
const sessionIds2 = await this.client.sMembers(activeSessionsKey);
|
|
5497
|
+
for (const sessionId of sessionIds2) {
|
|
5498
|
+
const channel = this.getChannel(sessionId);
|
|
5499
|
+
if (!this.client.publish) {
|
|
5500
|
+
throw new Error(
|
|
5501
|
+
"[RedisStreamManager] Redis client does not support publish method"
|
|
5502
|
+
);
|
|
5503
|
+
}
|
|
5504
|
+
await this.client.publish(channel, data);
|
|
5505
|
+
}
|
|
5506
|
+
} else {
|
|
5507
|
+
const pattern = `available:${this.prefix}*`;
|
|
5508
|
+
const keys = await this.client.keys(pattern);
|
|
5509
|
+
for (const key of keys) {
|
|
5510
|
+
const sessionId = key.replace(`available:${this.prefix}`, "");
|
|
5511
|
+
const channel = this.getChannel(sessionId);
|
|
5512
|
+
if (!this.client.publish) {
|
|
5513
|
+
throw new Error(
|
|
5514
|
+
"[RedisStreamManager] Redis client does not support publish method"
|
|
5515
|
+
);
|
|
5516
|
+
}
|
|
5517
|
+
await this.client.publish(channel, data);
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
} else {
|
|
5521
|
+
for (const sessionId of sessionIds) {
|
|
5522
|
+
const channel = this.getChannel(sessionId);
|
|
5523
|
+
if (!this.client.publish) {
|
|
5524
|
+
throw new Error(
|
|
5525
|
+
"[RedisStreamManager] Redis client does not support publish method"
|
|
5526
|
+
);
|
|
5527
|
+
}
|
|
5528
|
+
await this.client.publish(channel, data);
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
5531
|
+
} catch (error2) {
|
|
5532
|
+
console.error(`[RedisStreamManager] Error sending to sessions:`, error2);
|
|
5533
|
+
throw error2;
|
|
5534
|
+
}
|
|
5535
|
+
}
|
|
5536
|
+
/**
|
|
5537
|
+
* Remove an active SSE stream
|
|
5538
|
+
*/
|
|
5539
|
+
async delete(sessionId) {
|
|
5540
|
+
try {
|
|
5541
|
+
const heartbeat = this.heartbeats.get(sessionId);
|
|
5542
|
+
if (heartbeat) {
|
|
5543
|
+
clearInterval(heartbeat);
|
|
5544
|
+
this.heartbeats.delete(sessionId);
|
|
5545
|
+
}
|
|
5546
|
+
const channel = this.getChannel(sessionId);
|
|
5547
|
+
const deleteChannel = `delete:${channel}`;
|
|
5548
|
+
if (!this.pubSubClient.unsubscribe) {
|
|
5549
|
+
throw new Error(
|
|
5550
|
+
"[RedisStreamManager] Redis client does not support unsubscribe method"
|
|
5551
|
+
);
|
|
5552
|
+
}
|
|
5553
|
+
await this.pubSubClient.unsubscribe(channel);
|
|
5554
|
+
await this.pubSubClient.unsubscribe(deleteChannel);
|
|
5555
|
+
if (!this.client.publish) {
|
|
5556
|
+
throw new Error(
|
|
5557
|
+
"[RedisStreamManager] Redis client does not support publish method"
|
|
5558
|
+
);
|
|
5559
|
+
}
|
|
5560
|
+
await this.client.publish(deleteChannel, "");
|
|
5561
|
+
await this.client.del(this.getAvailableKey(sessionId));
|
|
5562
|
+
const activeSessionsKey = this.getActiveSessionsKey();
|
|
5563
|
+
if (this.client.sRem) {
|
|
5564
|
+
await this.client.sRem(activeSessionsKey, sessionId);
|
|
5565
|
+
}
|
|
5566
|
+
const controller = this.localControllers.get(sessionId);
|
|
5567
|
+
if (controller) {
|
|
5568
|
+
try {
|
|
5569
|
+
controller.close();
|
|
5570
|
+
} catch (error2) {
|
|
5571
|
+
console.debug(
|
|
5572
|
+
`[RedisStreamManager] Controller already closed for ${sessionId}`
|
|
5573
|
+
);
|
|
5574
|
+
}
|
|
5575
|
+
this.localControllers.delete(sessionId);
|
|
5576
|
+
}
|
|
5577
|
+
console.log(
|
|
5578
|
+
`[RedisStreamManager] Deleted stream for session ${sessionId}`
|
|
5579
|
+
);
|
|
5580
|
+
} catch (error2) {
|
|
5581
|
+
console.error(
|
|
5582
|
+
`[RedisStreamManager] Error deleting stream for ${sessionId}:`,
|
|
5583
|
+
error2
|
|
5584
|
+
);
|
|
5585
|
+
throw error2;
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5588
|
+
/**
|
|
5589
|
+
* Check if a session has an active stream (on ANY server)
|
|
5590
|
+
*/
|
|
5591
|
+
async has(sessionId) {
|
|
5592
|
+
try {
|
|
5593
|
+
const availableKey = this.getAvailableKey(sessionId);
|
|
5594
|
+
const exists = await this.client.exists(availableKey);
|
|
5595
|
+
return exists === 1;
|
|
5596
|
+
} catch (error2) {
|
|
5597
|
+
console.error(
|
|
5598
|
+
`[RedisStreamManager] Error checking session ${sessionId}:`,
|
|
5599
|
+
error2
|
|
5600
|
+
);
|
|
5601
|
+
return false;
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
/**
|
|
5605
|
+
* Close all connections and cleanup
|
|
5606
|
+
*/
|
|
5607
|
+
async close() {
|
|
5608
|
+
try {
|
|
5609
|
+
for (const heartbeat of this.heartbeats.values()) {
|
|
5610
|
+
clearInterval(heartbeat);
|
|
5611
|
+
}
|
|
5612
|
+
this.heartbeats.clear();
|
|
5613
|
+
const activeSessionsKey = this.getActiveSessionsKey();
|
|
5614
|
+
const sessionIdsToCleanup = Array.from(this.localControllers.keys());
|
|
5615
|
+
for (const sessionId of sessionIdsToCleanup) {
|
|
5616
|
+
await this.client.del(this.getAvailableKey(sessionId));
|
|
5617
|
+
if (this.client.sRem) {
|
|
5618
|
+
await this.client.sRem(activeSessionsKey, sessionId);
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5621
|
+
for (const controller of this.localControllers.values()) {
|
|
5622
|
+
try {
|
|
5623
|
+
controller.close();
|
|
5624
|
+
} catch (error2) {
|
|
5625
|
+
}
|
|
5626
|
+
}
|
|
5627
|
+
this.localControllers.clear();
|
|
5628
|
+
console.log(`[RedisStreamManager] Closed all streams`);
|
|
5629
|
+
} catch (error2) {
|
|
5630
|
+
console.error(`[RedisStreamManager] Error during close:`, error2);
|
|
5631
|
+
throw error2;
|
|
5632
|
+
}
|
|
5633
|
+
}
|
|
5634
|
+
/**
|
|
5635
|
+
* Get count of active local streams on this server instance
|
|
5636
|
+
*/
|
|
5637
|
+
get localSize() {
|
|
5638
|
+
return this.localControllers.size;
|
|
5639
|
+
}
|
|
5640
|
+
};
|
|
5641
|
+
|
|
5642
|
+
// src/server/endpoints/mount-mcp.ts
|
|
5643
|
+
init_runtime();
|
|
5644
|
+
init_telemetry2();
|
|
5645
|
+
var import_node_path2 = require("path");
|
|
5646
|
+
async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMode2) {
|
|
5647
|
+
const { FetchStreamableHTTPServerTransport } = await import("@mcp-use/modelcontextprotocol-sdk/experimental/fetch-streamable-http/index.js");
|
|
5648
|
+
const idleTimeoutMs = config.sessionIdleTimeoutMs ?? 3e5;
|
|
5649
|
+
const sessionStore = config.sessionStore ?? (isProductionMode2 ? new InMemorySessionStore() : new FileSystemSessionStore({
|
|
5650
|
+
path: (0, import_node_path2.join)(process.cwd(), ".mcp-use", "sessions.json")
|
|
5651
|
+
}));
|
|
5652
|
+
const streamManager = config.streamManager ?? new InMemoryStreamManager();
|
|
5653
|
+
const transports = /* @__PURE__ */ new Map();
|
|
5654
|
+
if (config.autoCreateSessionOnInvalidId !== void 0) {
|
|
5655
|
+
console.warn(
|
|
5656
|
+
"[MCP] WARNING: 'autoCreateSessionOnInvalidId' is deprecated and will be removed in a future version.\nThe MCP specification requires clients to send a new InitializeRequest when receiving a 404 for stale sessions.\nModern MCP clients handle this correctly. For session persistence across restarts, use the 'sessionStore' option.\nSee: https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#session-management"
|
|
5657
|
+
);
|
|
5658
|
+
}
|
|
5659
|
+
let idleCleanupInterval;
|
|
5660
|
+
if (!config.stateless && idleTimeoutMs > 0) {
|
|
5661
|
+
idleCleanupInterval = startIdleCleanup(
|
|
5662
|
+
sessions,
|
|
5663
|
+
idleTimeoutMs,
|
|
5664
|
+
transports,
|
|
5665
|
+
mcpServerInstance
|
|
5666
|
+
);
|
|
5667
|
+
}
|
|
5668
|
+
const handleRequest = /* @__PURE__ */ __name(async (c) => {
|
|
5669
|
+
const acceptHeader = c.req.header("Accept") || c.req.header("accept") || "";
|
|
5670
|
+
const clientSupportsSSE = acceptHeader.includes("text/event-stream");
|
|
5671
|
+
const useStatelessMode = config.stateless || !clientSupportsSSE;
|
|
5672
|
+
if (useStatelessMode) {
|
|
5673
|
+
const server = mcpServerInstance.getServerForSession();
|
|
5674
|
+
const transport = new FetchStreamableHTTPServerTransport({
|
|
5675
|
+
sessionIdGenerator: void 0,
|
|
5676
|
+
// No session tracking
|
|
5677
|
+
// Enable plain JSON responses ONLY if client doesn't support SSE
|
|
5678
|
+
// This allows k6/curl to work while maintaining SSE format for compatible clients
|
|
5679
|
+
enableJsonResponse: !clientSupportsSSE
|
|
5680
|
+
});
|
|
5681
|
+
try {
|
|
5682
|
+
await server.connect(transport);
|
|
5683
|
+
const request = c.req.raw;
|
|
5684
|
+
if (!clientSupportsSSE) {
|
|
5685
|
+
const modifiedRequest = new Request(request.url, {
|
|
5686
|
+
method: request.method,
|
|
5687
|
+
headers: {
|
|
5688
|
+
...Object.fromEntries(request.headers.entries()),
|
|
5689
|
+
Accept: "application/json, text/event-stream"
|
|
5690
|
+
},
|
|
5691
|
+
body: request.body,
|
|
5692
|
+
...request.body && { duplex: "half" }
|
|
5693
|
+
});
|
|
5694
|
+
return await transport.handleRequest(modifiedRequest);
|
|
5695
|
+
}
|
|
5696
|
+
return await transport.handleRequest(request);
|
|
5697
|
+
} catch (error2) {
|
|
5698
|
+
console.error("[MCP] Stateless request error:", error2);
|
|
5699
|
+
transport.close();
|
|
5700
|
+
server.close();
|
|
5701
|
+
throw error2;
|
|
5702
|
+
}
|
|
5703
|
+
} else {
|
|
5704
|
+
const sessionId = c.req.header("mcp-session-id");
|
|
5705
|
+
if (c.req.method === "HEAD") {
|
|
5706
|
+
if (sessionId && await sessionStore.has(sessionId)) {
|
|
5707
|
+
const session = await sessionStore.get(sessionId);
|
|
5708
|
+
if (session) {
|
|
5709
|
+
session.lastAccessedAt = Date.now();
|
|
5710
|
+
await sessionStore.set(sessionId, session);
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5713
|
+
return new Response(null, { status: 200 });
|
|
5714
|
+
}
|
|
5715
|
+
if (sessionId && await sessionStore.has(sessionId) && !transports.has(sessionId)) {
|
|
5716
|
+
console.log(
|
|
5717
|
+
`[MCP] Session metadata found but transport lost (likely hot reload): ${sessionId} - recreating transport`
|
|
5718
|
+
);
|
|
5719
|
+
const server2 = mcpServerInstance.getServerForSession();
|
|
5720
|
+
const transport2 = new FetchStreamableHTTPServerTransport({
|
|
5721
|
+
sessionIdGenerator: /* @__PURE__ */ __name(() => sessionId, "sessionIdGenerator"),
|
|
5722
|
+
// Reuse existing session ID
|
|
5723
|
+
onsessioninitialized: /* @__PURE__ */ __name(async (sid) => {
|
|
5724
|
+
console.log(`[MCP] Session reconnected: ${sid}`);
|
|
5725
|
+
transports.set(sid, transport2);
|
|
5726
|
+
const metadata = await sessionStore.get(sid);
|
|
5727
|
+
const sessionData = {
|
|
5728
|
+
transport: transport2,
|
|
5729
|
+
server: server2,
|
|
5730
|
+
lastAccessedAt: Date.now(),
|
|
5731
|
+
context: c,
|
|
5732
|
+
honoContext: c,
|
|
5733
|
+
...metadata || {}
|
|
5734
|
+
};
|
|
5735
|
+
sessions.set(sid, sessionData);
|
|
5736
|
+
server2.server.oninitialized = async () => {
|
|
5737
|
+
const clientCapabilities = server2.server.getClientCapabilities();
|
|
5738
|
+
const clientInfo = server2.server.getClientInfo?.() || {};
|
|
5739
|
+
const protocolVersion = server2.server.getProtocolVersion?.() || "unknown";
|
|
5740
|
+
const metadata2 = await sessionStore.get(sid);
|
|
5741
|
+
if (metadata2) {
|
|
5742
|
+
metadata2.clientCapabilities = clientCapabilities;
|
|
5743
|
+
metadata2.clientInfo = clientInfo;
|
|
5744
|
+
metadata2.protocolVersion = String(protocolVersion);
|
|
5745
|
+
await sessionStore.set(sid, metadata2);
|
|
5746
|
+
}
|
|
5747
|
+
const sessionData2 = sessions.get(sid);
|
|
5748
|
+
if (sessionData2) {
|
|
5749
|
+
sessionData2.clientCapabilities = clientCapabilities;
|
|
5750
|
+
}
|
|
5751
|
+
Telemetry.getInstance().trackServerInitialize({
|
|
5752
|
+
protocolVersion: String(protocolVersion),
|
|
5753
|
+
clientInfo: clientInfo || {},
|
|
5754
|
+
clientCapabilities: clientCapabilities || {},
|
|
5755
|
+
sessionId: sid
|
|
5756
|
+
}).catch(
|
|
5757
|
+
(e) => console.debug(`Failed to track server initialize: ${e}`)
|
|
5758
|
+
);
|
|
5759
|
+
if (!isProductionMode2) {
|
|
5760
|
+
console.log(
|
|
5761
|
+
`[MCP] Development mode: Sending list_changed notifications to reconnected session ${sid}`
|
|
5762
|
+
);
|
|
5763
|
+
try {
|
|
5764
|
+
const { sendNotificationToSession: sendNotificationToSession3 } = await Promise.resolve().then(() => (init_notifications(), notifications_exports));
|
|
5765
|
+
await sendNotificationToSession3(
|
|
5766
|
+
sessions,
|
|
5767
|
+
sid,
|
|
5768
|
+
"notifications/tools/list_changed"
|
|
5769
|
+
);
|
|
5770
|
+
await sendNotificationToSession3(
|
|
5771
|
+
sessions,
|
|
5772
|
+
sid,
|
|
5773
|
+
"notifications/resources/list_changed"
|
|
5774
|
+
);
|
|
5775
|
+
await sendNotificationToSession3(
|
|
5776
|
+
sessions,
|
|
5777
|
+
sid,
|
|
5778
|
+
"notifications/prompts/list_changed"
|
|
5779
|
+
);
|
|
5780
|
+
} catch (err) {
|
|
5781
|
+
console.debug(
|
|
5782
|
+
`[MCP] Failed to send list_changed notification:`,
|
|
5783
|
+
err
|
|
5784
|
+
);
|
|
5785
|
+
}
|
|
5786
|
+
}
|
|
5787
|
+
};
|
|
5788
|
+
}, "onsessioninitialized"),
|
|
5789
|
+
onsessionclosed: /* @__PURE__ */ __name(async (sid) => {
|
|
5790
|
+
console.log(`[MCP] Session closed: ${sid}`);
|
|
5791
|
+
transports.delete(sid);
|
|
5792
|
+
await streamManager.delete(sid);
|
|
5793
|
+
await sessionStore.delete(sid);
|
|
5794
|
+
sessions.delete(sid);
|
|
5795
|
+
mcpServerInstance.cleanupSessionSubscriptions?.(sid);
|
|
5796
|
+
}, "onsessionclosed")
|
|
5797
|
+
});
|
|
5798
|
+
await server2.connect(transport2);
|
|
5799
|
+
return transport2.handleRequest(c.req.raw);
|
|
5800
|
+
}
|
|
5801
|
+
if (sessionId && !await sessionStore.has(sessionId)) {
|
|
5802
|
+
console.log(
|
|
5803
|
+
`[MCP] Session not found: ${sessionId} - returning 404 (client should re-initialize)`
|
|
5804
|
+
);
|
|
5805
|
+
return c.json(
|
|
5806
|
+
{
|
|
5807
|
+
jsonrpc: "2.0",
|
|
5808
|
+
error: { code: -32001, message: "Session not found" },
|
|
5809
|
+
id: null
|
|
5810
|
+
},
|
|
5811
|
+
404
|
|
5812
|
+
);
|
|
5813
|
+
}
|
|
5814
|
+
if (sessionId && transports.has(sessionId)) {
|
|
5815
|
+
const transport2 = transports.get(sessionId);
|
|
5816
|
+
const metadata = await sessionStore.get(sessionId);
|
|
5817
|
+
if (metadata) {
|
|
5818
|
+
metadata.lastAccessedAt = Date.now();
|
|
5819
|
+
await sessionStore.set(sessionId, metadata);
|
|
5820
|
+
}
|
|
5821
|
+
const sessionData = sessions.get(sessionId);
|
|
5822
|
+
if (sessionData) {
|
|
5823
|
+
sessionData.lastAccessedAt = Date.now();
|
|
5824
|
+
sessionData.context = c;
|
|
5825
|
+
sessionData.honoContext = c;
|
|
5826
|
+
}
|
|
5827
|
+
return transport2.handleRequest(c.req.raw);
|
|
5828
|
+
}
|
|
5829
|
+
const server = mcpServerInstance.getServerForSession();
|
|
4704
5830
|
const transport = new FetchStreamableHTTPServerTransport({
|
|
4705
5831
|
sessionIdGenerator: /* @__PURE__ */ __name(() => generateUUID(), "sessionIdGenerator"),
|
|
4706
|
-
onsessioninitialized: /* @__PURE__ */ __name((sid) => {
|
|
5832
|
+
onsessioninitialized: /* @__PURE__ */ __name(async (sid) => {
|
|
4707
5833
|
console.log(`[MCP] Session initialized: ${sid}`);
|
|
4708
5834
|
transports.set(sid, transport);
|
|
4709
|
-
|
|
5835
|
+
const sessionData = {
|
|
4710
5836
|
transport,
|
|
4711
5837
|
server,
|
|
4712
5838
|
lastAccessedAt: Date.now(),
|
|
4713
5839
|
context: c,
|
|
4714
5840
|
honoContext: c
|
|
5841
|
+
};
|
|
5842
|
+
sessions.set(sid, sessionData);
|
|
5843
|
+
await sessionStore.set(sid, {
|
|
5844
|
+
lastAccessedAt: Date.now()
|
|
4715
5845
|
});
|
|
4716
|
-
server.server.oninitialized = () => {
|
|
5846
|
+
server.server.oninitialized = async () => {
|
|
4717
5847
|
const clientCapabilities = server.server.getClientCapabilities();
|
|
4718
5848
|
const clientInfo = server.server.getClientInfo?.() || {};
|
|
4719
5849
|
const protocolVersion = server.server.getProtocolVersion?.() || "unknown";
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
5850
|
+
const metadata = await sessionStore.get(sid);
|
|
5851
|
+
if (metadata) {
|
|
5852
|
+
metadata.clientCapabilities = clientCapabilities;
|
|
5853
|
+
metadata.clientInfo = clientInfo;
|
|
5854
|
+
metadata.protocolVersion = String(protocolVersion);
|
|
5855
|
+
await sessionStore.set(sid, metadata);
|
|
4723
5856
|
console.log(
|
|
4724
5857
|
`[MCP] Captured client capabilities for session ${sid}:`,
|
|
4725
|
-
Object.keys(clientCapabilities)
|
|
5858
|
+
clientCapabilities ? Object.keys(clientCapabilities) : "none"
|
|
4726
5859
|
);
|
|
4727
5860
|
}
|
|
5861
|
+
const sessionData2 = sessions.get(sid);
|
|
5862
|
+
if (sessionData2) {
|
|
5863
|
+
sessionData2.clientCapabilities = clientCapabilities;
|
|
5864
|
+
}
|
|
4728
5865
|
Telemetry.getInstance().trackServerInitialize({
|
|
4729
5866
|
protocolVersion: String(protocolVersion),
|
|
4730
5867
|
clientInfo: clientInfo || {},
|
|
@@ -4735,9 +5872,11 @@ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMo
|
|
|
4735
5872
|
);
|
|
4736
5873
|
};
|
|
4737
5874
|
}, "onsessioninitialized"),
|
|
4738
|
-
onsessionclosed: /* @__PURE__ */ __name((sid) => {
|
|
5875
|
+
onsessionclosed: /* @__PURE__ */ __name(async (sid) => {
|
|
4739
5876
|
console.log(`[MCP] Session closed: ${sid}`);
|
|
4740
5877
|
transports.delete(sid);
|
|
5878
|
+
await streamManager.delete(sid);
|
|
5879
|
+
await sessionStore.delete(sid);
|
|
4741
5880
|
sessions.delete(sid);
|
|
4742
5881
|
mcpServerInstance.cleanupSessionSubscriptions?.(sid);
|
|
4743
5882
|
}, "onsessionclosed")
|
|
@@ -4747,7 +5886,7 @@ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMo
|
|
|
4747
5886
|
}
|
|
4748
5887
|
}, "handleRequest");
|
|
4749
5888
|
for (const endpoint of ["/mcp", "/sse"]) {
|
|
4750
|
-
app.on(["GET", "POST", "DELETE"], endpoint, handleRequest);
|
|
5889
|
+
app.on(["GET", "POST", "DELETE", "HEAD"], endpoint, handleRequest);
|
|
4751
5890
|
}
|
|
4752
5891
|
console.log(
|
|
4753
5892
|
`[MCP] Server mounted at /mcp and /sse (${config.stateless ? "stateless" : "stateful"} mode)`
|
|
@@ -5059,6 +6198,7 @@ var MCPServerClass = class {
|
|
|
5059
6198
|
serverPort;
|
|
5060
6199
|
serverHost;
|
|
5061
6200
|
serverBaseUrl;
|
|
6201
|
+
favicon;
|
|
5062
6202
|
registeredTools = [];
|
|
5063
6203
|
registeredPrompts = [];
|
|
5064
6204
|
registeredResources = [];
|
|
@@ -5124,6 +6264,7 @@ var MCPServerClass = class {
|
|
|
5124
6264
|
}
|
|
5125
6265
|
this.serverHost = config.host || "localhost";
|
|
5126
6266
|
this.serverBaseUrl = config.baseUrl;
|
|
6267
|
+
this.favicon = config.favicon;
|
|
5127
6268
|
this.nativeServer = new import_mcp2.McpServer(
|
|
5128
6269
|
{
|
|
5129
6270
|
name: config.name,
|
|
@@ -5188,7 +6329,10 @@ var MCPServerClass = class {
|
|
|
5188
6329
|
"openai/widgetAccessible": widgetConfig.widgetAccessible ?? true,
|
|
5189
6330
|
"openai/resultCanProduceWidget": widgetConfig.resultCanProduceWidget ?? true
|
|
5190
6331
|
};
|
|
5191
|
-
result._meta =
|
|
6332
|
+
result._meta = {
|
|
6333
|
+
...result._meta || {},
|
|
6334
|
+
...responseMeta
|
|
6335
|
+
};
|
|
5192
6336
|
if (result.content?.[0]?.type === "text" && !result.content[0].text) {
|
|
5193
6337
|
result.content[0].text = `Displaying ${widgetName}`;
|
|
5194
6338
|
}
|
|
@@ -5591,6 +6735,9 @@ var MCPServerClass = class {
|
|
|
5591
6735
|
getActiveSessions = getActiveSessions;
|
|
5592
6736
|
sendNotification = sendNotification;
|
|
5593
6737
|
sendNotificationToSession = sendNotificationToSession2;
|
|
6738
|
+
sendToolsListChanged = sendToolsListChanged;
|
|
6739
|
+
sendResourcesListChanged = sendResourcesListChanged;
|
|
6740
|
+
sendPromptsListChanged = sendPromptsListChanged;
|
|
5594
6741
|
/**
|
|
5595
6742
|
* Notify subscribed clients that a resource has been updated
|
|
5596
6743
|
*
|
|
@@ -5843,7 +6990,8 @@ function createMCPServer(name, config = {}) {
|
|
|
5843
6990
|
allowedOrigins: config.allowedOrigins,
|
|
5844
6991
|
sessionIdleTimeoutMs: config.sessionIdleTimeoutMs,
|
|
5845
6992
|
autoCreateSessionOnInvalidId: config.autoCreateSessionOnInvalidId,
|
|
5846
|
-
oauth: config.oauth
|
|
6993
|
+
oauth: config.oauth,
|
|
6994
|
+
favicon: config.favicon
|
|
5847
6995
|
});
|
|
5848
6996
|
return instance;
|
|
5849
6997
|
}
|