@hirohsu/user-web-feedback 2.8.10 → 2.8.12
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/cli.cjs +74 -26
- package/dist/index.cjs +74 -26
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -105763,6 +105763,8 @@ var WebServer = class {
|
|
|
105763
105763
|
sseTransports = /* @__PURE__ */ new Map();
|
|
105764
105764
|
sseTransportsList = [];
|
|
105765
105765
|
pendingDeliveryCache = /* @__PURE__ */ new Map();
|
|
105766
|
+
streamableHttpTransports = /* @__PURE__ */ new Map();
|
|
105767
|
+
streamableHttpSseActive = /* @__PURE__ */ new Map();
|
|
105766
105768
|
dbInitialized = false;
|
|
105767
105769
|
selfProbeService;
|
|
105768
105770
|
/**
|
|
@@ -107762,6 +107764,17 @@ var WebServer = class {
|
|
|
107762
107764
|
/**
|
|
107763
107765
|
* 處理回饋提交
|
|
107764
107766
|
*/
|
|
107767
|
+
async waitForActiveConnection(maxRounds = 5, intervalMs = 5e3) {
|
|
107768
|
+
for (let round = 1; round <= maxRounds; round++) {
|
|
107769
|
+
const hasStreamableSSE = [...this.streamableHttpSseActive.values()].some((v) => v);
|
|
107770
|
+
const hasSSETransport = this.sseTransportsList.length > 0;
|
|
107771
|
+
if (hasStreamableSSE || hasSSETransport) return true;
|
|
107772
|
+
logger.warn(`[\u9023\u7DDA\u7B49\u5F85] \u7B2C ${round}/${maxRounds} \u8F2A\uFF1A\u76EE\u524D\u7121\u6D3B\u8E8D MCP Client \u9023\u7DDA\uFF0C${intervalMs / 1e3}s \u5F8C\u91CD\u8A66...`);
|
|
107773
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
107774
|
+
}
|
|
107775
|
+
logger.error(`[\u9023\u7DDA\u7B49\u5F85] \u5DF2\u5617\u8A66 ${maxRounds} \u8F2A\u4ECD\u7121\u6D3B\u8E8D\u9023\u7DDA\uFF0C\u5C07\u76F4\u63A5\u9001\u51FA\uFF08\u5FEB\u53D6\u6A5F\u5236\u4F5C\u70BA\u4FDD\u8B77\uFF09`);
|
|
107776
|
+
return false;
|
|
107777
|
+
}
|
|
107765
107778
|
async handleFeedbackSubmission(socket, feedbackData) {
|
|
107766
107779
|
const session = this.sessionStorage.getSession(feedbackData.sessionId);
|
|
107767
107780
|
if (!session) {
|
|
@@ -107804,6 +107817,7 @@ var WebServer = class {
|
|
|
107804
107817
|
this.emitDashboardSessionUpdated(session.projectId, feedbackData.sessionId, "completed", session.workSummary);
|
|
107805
107818
|
}
|
|
107806
107819
|
if (session.resolve) {
|
|
107820
|
+
this.sessionStorage.updateSession(feedbackData.sessionId, { resolved: true });
|
|
107807
107821
|
if (session.projectId) {
|
|
107808
107822
|
const cachedResult2 = {
|
|
107809
107823
|
feedback: session.feedback,
|
|
@@ -107819,6 +107833,10 @@ var WebServer = class {
|
|
|
107819
107833
|
const projectId = session.projectId;
|
|
107820
107834
|
setTimeout(() => this.pendingDeliveryCache.delete(projectId), 6e4);
|
|
107821
107835
|
}
|
|
107836
|
+
const hasConnection = await this.waitForActiveConnection();
|
|
107837
|
+
if (hasConnection) {
|
|
107838
|
+
logger.info("[\u9023\u7DDA\u76E3\u63A7] \u9023\u7DDA\u78BA\u8A8D\uFF0C\u6B63\u5728\u9001\u51FA\u56DE\u8986\u81F3 MCP Client...");
|
|
107839
|
+
}
|
|
107822
107840
|
session.resolve(session.feedback);
|
|
107823
107841
|
const sessionIdToDelete = feedbackData.sessionId;
|
|
107824
107842
|
setTimeout(() => this.sessionStorage.deleteSession(sessionIdToDelete), 5e3);
|
|
@@ -107909,10 +107927,13 @@ var WebServer = class {
|
|
|
107909
107927
|
logger.info(`\u5EFA\u7ACB\u56DE\u994B\u6703\u8A71: ${sessionId}, \u903E\u6642: ${timeoutSeconds}\u79D2, \u5C08\u6848: ${project.name} (${project.id})`);
|
|
107910
107928
|
const feedbackUrl = this.generateFeedbackUrl(sessionId);
|
|
107911
107929
|
const pendingEntry = this.pendingDeliveryCache.get(project.id);
|
|
107912
|
-
if (pendingEntry && pendingEntry.expiresAt > Date.now()
|
|
107913
|
-
|
|
107914
|
-
|
|
107915
|
-
|
|
107930
|
+
if (pendingEntry && pendingEntry.expiresAt > Date.now()) {
|
|
107931
|
+
const prevSession = this.sessionStorage.getSession(pendingEntry.result.sessionId);
|
|
107932
|
+
if (!prevSession || prevSession.resolved) {
|
|
107933
|
+
logger.warn(`[\u91CD\u8A66\u88DC\u511F] \u5075\u6E2C\u5230\u5C08\u6848 "${project.name}" \u5B58\u5728\u672A\u78BA\u8A8D\u9001\u9054\u7684\u56DE\u8986 (sessionId: ${pendingEntry.result.sessionId})\uFF0C\u76F4\u63A5\u56DE\u50B3\u5FEB\u53D6\u7D50\u679C`);
|
|
107934
|
+
this.pendingDeliveryCache.delete(project.id);
|
|
107935
|
+
return pendingEntry.result;
|
|
107936
|
+
}
|
|
107916
107937
|
}
|
|
107917
107938
|
return new Promise((resolve2, reject) => {
|
|
107918
107939
|
const session = {
|
|
@@ -108211,7 +108232,6 @@ var WebServer = class {
|
|
|
108211
108232
|
*/
|
|
108212
108233
|
async setupStreamableHTTPEndpoints() {
|
|
108213
108234
|
const { StreamableHTTPServerTransport: StreamableHTTPServerTransport2 } = await Promise.resolve().then(() => (init_streamableHttp2(), streamableHttp_exports2));
|
|
108214
|
-
const streamableTransports = /* @__PURE__ */ new Map();
|
|
108215
108235
|
this.app.post("/mcp", import_express.default.json(), async (req, res) => {
|
|
108216
108236
|
logger.debug("\u6536\u5230 Streamable HTTP \u8ACB\u6C42:", JSON.stringify(req.body));
|
|
108217
108237
|
if (!this.mcpServerRef) {
|
|
@@ -108222,7 +108242,7 @@ var WebServer = class {
|
|
|
108222
108242
|
req.socket?.setKeepAlive(true, 3e4);
|
|
108223
108243
|
req.on("close", () => {
|
|
108224
108244
|
if (!res.writableEnded) {
|
|
108225
|
-
logger.warn("[\
|
|
108245
|
+
logger.warn("[\u9023\u7DDA\u76E3\u63A7] MCP Client HTTP \u9023\u7DDA\u5728\u5DE5\u5177\u57F7\u884C\u671F\u9593\u63D0\u524D\u95DC\u9589\uFF0C\u56DE\u8986\u53EF\u80FD\u672A\u9001\u9054");
|
|
108226
108246
|
}
|
|
108227
108247
|
});
|
|
108228
108248
|
const sessionId = req.headers["mcp-session-id"];
|
|
@@ -108230,27 +108250,34 @@ var WebServer = class {
|
|
|
108230
108250
|
if (isInitialize) {
|
|
108231
108251
|
const transport = new StreamableHTTPServerTransport2({
|
|
108232
108252
|
sessionIdGenerator: () => `http-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
108233
|
-
enableJsonResponse:
|
|
108253
|
+
enableJsonResponse: false
|
|
108234
108254
|
});
|
|
108235
108255
|
transport.onclose = () => {
|
|
108236
108256
|
const sid = transport.sessionId;
|
|
108237
|
-
if (sid)
|
|
108238
|
-
|
|
108257
|
+
if (sid) {
|
|
108258
|
+
this.streamableHttpTransports.delete(sid);
|
|
108259
|
+
this.streamableHttpSseActive.delete(sid);
|
|
108260
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Client \u5DF2\u65B7\u7DDA (sessionId=${sid})\uFF0C\u5269\u9918\u6D3B\u8E8D\u9023\u7DDA: ${this.streamableHttpTransports.size}`);
|
|
108261
|
+
}
|
|
108239
108262
|
};
|
|
108240
108263
|
try {
|
|
108241
108264
|
await this.mcpServerRef.getMcpServerInstance().connect(transport);
|
|
108242
|
-
|
|
108265
|
+
const clientIp = req.headers["x-forwarded-for"] || req.socket?.remoteAddress || "unknown";
|
|
108266
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] \u2705 MCP Client \u5DF2\u9023\u7DDA \u2014 IP: ${clientIp}`);
|
|
108243
108267
|
await transport.handleRequest(req, res, req.body);
|
|
108244
108268
|
const sid = transport.sessionId;
|
|
108245
|
-
if (sid)
|
|
108269
|
+
if (sid) {
|
|
108270
|
+
this.streamableHttpTransports.set(sid, transport);
|
|
108271
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Session \u5DF2\u5EFA\u7ACB (sessionId=${sid})\uFF0C\u76EE\u524D\u6D3B\u8E8D\u9023\u7DDA: ${this.streamableHttpTransports.size}`);
|
|
108272
|
+
}
|
|
108246
108273
|
} catch (error2) {
|
|
108247
108274
|
logger.error("\u8655\u7406 Streamable HTTP \u521D\u59CB\u5316\u8ACB\u6C42\u5931\u6557:", error2);
|
|
108248
108275
|
if (!res.headersSent) {
|
|
108249
108276
|
res.status(500).json({ error: "Failed to initialize MCP session" });
|
|
108250
108277
|
}
|
|
108251
108278
|
}
|
|
108252
|
-
} else if (sessionId &&
|
|
108253
|
-
const transport =
|
|
108279
|
+
} else if (sessionId && this.streamableHttpTransports.has(sessionId)) {
|
|
108280
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
108254
108281
|
try {
|
|
108255
108282
|
await transport.handleRequest(req, res, req.body);
|
|
108256
108283
|
} catch (error2) {
|
|
@@ -108265,31 +108292,52 @@ var WebServer = class {
|
|
|
108265
108292
|
});
|
|
108266
108293
|
this.app.delete("/mcp", async (req, res) => {
|
|
108267
108294
|
const sessionId = req.headers["mcp-session-id"];
|
|
108268
|
-
if (sessionId &&
|
|
108269
|
-
const transport =
|
|
108295
|
+
if (sessionId && this.streamableHttpTransports.has(sessionId)) {
|
|
108296
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
108270
108297
|
try {
|
|
108271
108298
|
await transport.handleRequest(req, res, req.body);
|
|
108272
108299
|
} catch {
|
|
108273
108300
|
}
|
|
108274
|
-
|
|
108301
|
+
this.streamableHttpTransports.delete(sessionId);
|
|
108302
|
+
this.streamableHttpSseActive.delete(sessionId);
|
|
108303
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Session \u5DF2\u522A\u9664 (sessionId=${sessionId})`);
|
|
108275
108304
|
} else {
|
|
108276
108305
|
res.status(404).json({ error: "Session not found" });
|
|
108277
108306
|
}
|
|
108278
108307
|
});
|
|
108279
108308
|
this.app.get("/mcp", async (req, res) => {
|
|
108280
108309
|
const sessionId = req.query["sessionId"];
|
|
108281
|
-
if (sessionId
|
|
108282
|
-
|
|
108310
|
+
if (!sessionId || !this.streamableHttpTransports.has(sessionId)) {
|
|
108311
|
+
res.status(400).json({ error: "Invalid session" });
|
|
108312
|
+
return;
|
|
108313
|
+
}
|
|
108314
|
+
this.streamableHttpSseActive.set(sessionId, true);
|
|
108315
|
+
const activeSseCount = [...this.streamableHttpSseActive.values()].filter(Boolean).length;
|
|
108316
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] \u{1F4E1} GET SSE \u4EA4\u4ED8\u901A\u9053\u5DF2\u958B\u555F (sessionId=${sessionId})\uFF0C\u6D3B\u8E8D SSE \u6578: ${activeSseCount}`);
|
|
108317
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
108318
|
+
const heartbeat = setInterval(() => {
|
|
108283
108319
|
try {
|
|
108284
|
-
|
|
108285
|
-
|
|
108286
|
-
|
|
108287
|
-
|
|
108288
|
-
res.status(500).json({ error: "Failed to process request" });
|
|
108289
|
-
}
|
|
108320
|
+
if (!res.writableEnded) res.write(": ping\n\n");
|
|
108321
|
+
else clearInterval(heartbeat);
|
|
108322
|
+
} catch {
|
|
108323
|
+
clearInterval(heartbeat);
|
|
108290
108324
|
}
|
|
108291
|
-
}
|
|
108292
|
-
|
|
108325
|
+
}, 15e3);
|
|
108326
|
+
req.on("close", () => {
|
|
108327
|
+
clearInterval(heartbeat);
|
|
108328
|
+
this.streamableHttpSseActive.set(sessionId, false);
|
|
108329
|
+
const remaining = [...this.streamableHttpSseActive.values()].filter(Boolean).length;
|
|
108330
|
+
logger.warn(`[\u9023\u7DDA\u76E3\u63A7] \u26A0\uFE0F GET SSE \u4EA4\u4ED8\u901A\u9053\u5DF2\u95DC\u9589 (sessionId=${sessionId})\uFF0C\u5269\u9918\u6D3B\u8E8D SSE \u6578: ${remaining}`);
|
|
108331
|
+
});
|
|
108332
|
+
try {
|
|
108333
|
+
await transport.handleRequest(req, res);
|
|
108334
|
+
} catch (error2) {
|
|
108335
|
+
logger.error("\u8655\u7406 Streamable HTTP GET \u8ACB\u6C42\u5931\u6557:", error2);
|
|
108336
|
+
if (!res.headersSent) {
|
|
108337
|
+
res.status(500).json({ error: "Failed to process request" });
|
|
108338
|
+
}
|
|
108339
|
+
} finally {
|
|
108340
|
+
clearInterval(heartbeat);
|
|
108293
108341
|
}
|
|
108294
108342
|
});
|
|
108295
108343
|
logger.info("Streamable HTTP \u7AEF\u9EDE\u5DF2\u8A2D\u5B9A: GET/POST/DELETE /mcp");
|
package/dist/index.cjs
CHANGED
|
@@ -102780,6 +102780,8 @@ var WebServer = class {
|
|
|
102780
102780
|
sseTransports = /* @__PURE__ */ new Map();
|
|
102781
102781
|
sseTransportsList = [];
|
|
102782
102782
|
pendingDeliveryCache = /* @__PURE__ */ new Map();
|
|
102783
|
+
streamableHttpTransports = /* @__PURE__ */ new Map();
|
|
102784
|
+
streamableHttpSseActive = /* @__PURE__ */ new Map();
|
|
102783
102785
|
dbInitialized = false;
|
|
102784
102786
|
selfProbeService;
|
|
102785
102787
|
/**
|
|
@@ -104779,6 +104781,17 @@ var WebServer = class {
|
|
|
104779
104781
|
/**
|
|
104780
104782
|
* 處理回饋提交
|
|
104781
104783
|
*/
|
|
104784
|
+
async waitForActiveConnection(maxRounds = 5, intervalMs = 5e3) {
|
|
104785
|
+
for (let round = 1; round <= maxRounds; round++) {
|
|
104786
|
+
const hasStreamableSSE = [...this.streamableHttpSseActive.values()].some((v) => v);
|
|
104787
|
+
const hasSSETransport = this.sseTransportsList.length > 0;
|
|
104788
|
+
if (hasStreamableSSE || hasSSETransport) return true;
|
|
104789
|
+
logger.warn(`[\u9023\u7DDA\u7B49\u5F85] \u7B2C ${round}/${maxRounds} \u8F2A\uFF1A\u76EE\u524D\u7121\u6D3B\u8E8D MCP Client \u9023\u7DDA\uFF0C${intervalMs / 1e3}s \u5F8C\u91CD\u8A66...`);
|
|
104790
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
104791
|
+
}
|
|
104792
|
+
logger.error(`[\u9023\u7DDA\u7B49\u5F85] \u5DF2\u5617\u8A66 ${maxRounds} \u8F2A\u4ECD\u7121\u6D3B\u8E8D\u9023\u7DDA\uFF0C\u5C07\u76F4\u63A5\u9001\u51FA\uFF08\u5FEB\u53D6\u6A5F\u5236\u4F5C\u70BA\u4FDD\u8B77\uFF09`);
|
|
104793
|
+
return false;
|
|
104794
|
+
}
|
|
104782
104795
|
async handleFeedbackSubmission(socket, feedbackData) {
|
|
104783
104796
|
const session = this.sessionStorage.getSession(feedbackData.sessionId);
|
|
104784
104797
|
if (!session) {
|
|
@@ -104821,6 +104834,7 @@ var WebServer = class {
|
|
|
104821
104834
|
this.emitDashboardSessionUpdated(session.projectId, feedbackData.sessionId, "completed", session.workSummary);
|
|
104822
104835
|
}
|
|
104823
104836
|
if (session.resolve) {
|
|
104837
|
+
this.sessionStorage.updateSession(feedbackData.sessionId, { resolved: true });
|
|
104824
104838
|
if (session.projectId) {
|
|
104825
104839
|
const cachedResult2 = {
|
|
104826
104840
|
feedback: session.feedback,
|
|
@@ -104836,6 +104850,10 @@ var WebServer = class {
|
|
|
104836
104850
|
const projectId = session.projectId;
|
|
104837
104851
|
setTimeout(() => this.pendingDeliveryCache.delete(projectId), 6e4);
|
|
104838
104852
|
}
|
|
104853
|
+
const hasConnection = await this.waitForActiveConnection();
|
|
104854
|
+
if (hasConnection) {
|
|
104855
|
+
logger.info("[\u9023\u7DDA\u76E3\u63A7] \u9023\u7DDA\u78BA\u8A8D\uFF0C\u6B63\u5728\u9001\u51FA\u56DE\u8986\u81F3 MCP Client...");
|
|
104856
|
+
}
|
|
104839
104857
|
session.resolve(session.feedback);
|
|
104840
104858
|
const sessionIdToDelete = feedbackData.sessionId;
|
|
104841
104859
|
setTimeout(() => this.sessionStorage.deleteSession(sessionIdToDelete), 5e3);
|
|
@@ -104926,10 +104944,13 @@ var WebServer = class {
|
|
|
104926
104944
|
logger.info(`\u5EFA\u7ACB\u56DE\u994B\u6703\u8A71: ${sessionId}, \u903E\u6642: ${timeoutSeconds}\u79D2, \u5C08\u6848: ${project.name} (${project.id})`);
|
|
104927
104945
|
const feedbackUrl = this.generateFeedbackUrl(sessionId);
|
|
104928
104946
|
const pendingEntry = this.pendingDeliveryCache.get(project.id);
|
|
104929
|
-
if (pendingEntry && pendingEntry.expiresAt > Date.now()
|
|
104930
|
-
|
|
104931
|
-
|
|
104932
|
-
|
|
104947
|
+
if (pendingEntry && pendingEntry.expiresAt > Date.now()) {
|
|
104948
|
+
const prevSession = this.sessionStorage.getSession(pendingEntry.result.sessionId);
|
|
104949
|
+
if (!prevSession || prevSession.resolved) {
|
|
104950
|
+
logger.warn(`[\u91CD\u8A66\u88DC\u511F] \u5075\u6E2C\u5230\u5C08\u6848 "${project.name}" \u5B58\u5728\u672A\u78BA\u8A8D\u9001\u9054\u7684\u56DE\u8986 (sessionId: ${pendingEntry.result.sessionId})\uFF0C\u76F4\u63A5\u56DE\u50B3\u5FEB\u53D6\u7D50\u679C`);
|
|
104951
|
+
this.pendingDeliveryCache.delete(project.id);
|
|
104952
|
+
return pendingEntry.result;
|
|
104953
|
+
}
|
|
104933
104954
|
}
|
|
104934
104955
|
return new Promise((resolve, reject) => {
|
|
104935
104956
|
const session = {
|
|
@@ -105228,7 +105249,6 @@ var WebServer = class {
|
|
|
105228
105249
|
*/
|
|
105229
105250
|
async setupStreamableHTTPEndpoints() {
|
|
105230
105251
|
const { StreamableHTTPServerTransport: StreamableHTTPServerTransport2 } = await Promise.resolve().then(() => (init_streamableHttp2(), streamableHttp_exports2));
|
|
105231
|
-
const streamableTransports = /* @__PURE__ */ new Map();
|
|
105232
105252
|
this.app.post("/mcp", import_express.default.json(), async (req, res) => {
|
|
105233
105253
|
logger.debug("\u6536\u5230 Streamable HTTP \u8ACB\u6C42:", JSON.stringify(req.body));
|
|
105234
105254
|
if (!this.mcpServerRef) {
|
|
@@ -105239,7 +105259,7 @@ var WebServer = class {
|
|
|
105239
105259
|
req.socket?.setKeepAlive(true, 3e4);
|
|
105240
105260
|
req.on("close", () => {
|
|
105241
105261
|
if (!res.writableEnded) {
|
|
105242
|
-
logger.warn("[\
|
|
105262
|
+
logger.warn("[\u9023\u7DDA\u76E3\u63A7] MCP Client HTTP \u9023\u7DDA\u5728\u5DE5\u5177\u57F7\u884C\u671F\u9593\u63D0\u524D\u95DC\u9589\uFF0C\u56DE\u8986\u53EF\u80FD\u672A\u9001\u9054");
|
|
105243
105263
|
}
|
|
105244
105264
|
});
|
|
105245
105265
|
const sessionId = req.headers["mcp-session-id"];
|
|
@@ -105247,27 +105267,34 @@ var WebServer = class {
|
|
|
105247
105267
|
if (isInitialize) {
|
|
105248
105268
|
const transport = new StreamableHTTPServerTransport2({
|
|
105249
105269
|
sessionIdGenerator: () => `http-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
105250
|
-
enableJsonResponse:
|
|
105270
|
+
enableJsonResponse: false
|
|
105251
105271
|
});
|
|
105252
105272
|
transport.onclose = () => {
|
|
105253
105273
|
const sid = transport.sessionId;
|
|
105254
|
-
if (sid)
|
|
105255
|
-
|
|
105274
|
+
if (sid) {
|
|
105275
|
+
this.streamableHttpTransports.delete(sid);
|
|
105276
|
+
this.streamableHttpSseActive.delete(sid);
|
|
105277
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Client \u5DF2\u65B7\u7DDA (sessionId=${sid})\uFF0C\u5269\u9918\u6D3B\u8E8D\u9023\u7DDA: ${this.streamableHttpTransports.size}`);
|
|
105278
|
+
}
|
|
105256
105279
|
};
|
|
105257
105280
|
try {
|
|
105258
105281
|
await this.mcpServerRef.getMcpServerInstance().connect(transport);
|
|
105259
|
-
|
|
105282
|
+
const clientIp = req.headers["x-forwarded-for"] || req.socket?.remoteAddress || "unknown";
|
|
105283
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] \u2705 MCP Client \u5DF2\u9023\u7DDA \u2014 IP: ${clientIp}`);
|
|
105260
105284
|
await transport.handleRequest(req, res, req.body);
|
|
105261
105285
|
const sid = transport.sessionId;
|
|
105262
|
-
if (sid)
|
|
105286
|
+
if (sid) {
|
|
105287
|
+
this.streamableHttpTransports.set(sid, transport);
|
|
105288
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Session \u5DF2\u5EFA\u7ACB (sessionId=${sid})\uFF0C\u76EE\u524D\u6D3B\u8E8D\u9023\u7DDA: ${this.streamableHttpTransports.size}`);
|
|
105289
|
+
}
|
|
105263
105290
|
} catch (error2) {
|
|
105264
105291
|
logger.error("\u8655\u7406 Streamable HTTP \u521D\u59CB\u5316\u8ACB\u6C42\u5931\u6557:", error2);
|
|
105265
105292
|
if (!res.headersSent) {
|
|
105266
105293
|
res.status(500).json({ error: "Failed to initialize MCP session" });
|
|
105267
105294
|
}
|
|
105268
105295
|
}
|
|
105269
|
-
} else if (sessionId &&
|
|
105270
|
-
const transport =
|
|
105296
|
+
} else if (sessionId && this.streamableHttpTransports.has(sessionId)) {
|
|
105297
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
105271
105298
|
try {
|
|
105272
105299
|
await transport.handleRequest(req, res, req.body);
|
|
105273
105300
|
} catch (error2) {
|
|
@@ -105282,31 +105309,52 @@ var WebServer = class {
|
|
|
105282
105309
|
});
|
|
105283
105310
|
this.app.delete("/mcp", async (req, res) => {
|
|
105284
105311
|
const sessionId = req.headers["mcp-session-id"];
|
|
105285
|
-
if (sessionId &&
|
|
105286
|
-
const transport =
|
|
105312
|
+
if (sessionId && this.streamableHttpTransports.has(sessionId)) {
|
|
105313
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
105287
105314
|
try {
|
|
105288
105315
|
await transport.handleRequest(req, res, req.body);
|
|
105289
105316
|
} catch {
|
|
105290
105317
|
}
|
|
105291
|
-
|
|
105318
|
+
this.streamableHttpTransports.delete(sessionId);
|
|
105319
|
+
this.streamableHttpSseActive.delete(sessionId);
|
|
105320
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Session \u5DF2\u522A\u9664 (sessionId=${sessionId})`);
|
|
105292
105321
|
} else {
|
|
105293
105322
|
res.status(404).json({ error: "Session not found" });
|
|
105294
105323
|
}
|
|
105295
105324
|
});
|
|
105296
105325
|
this.app.get("/mcp", async (req, res) => {
|
|
105297
105326
|
const sessionId = req.query["sessionId"];
|
|
105298
|
-
if (sessionId
|
|
105299
|
-
|
|
105327
|
+
if (!sessionId || !this.streamableHttpTransports.has(sessionId)) {
|
|
105328
|
+
res.status(400).json({ error: "Invalid session" });
|
|
105329
|
+
return;
|
|
105330
|
+
}
|
|
105331
|
+
this.streamableHttpSseActive.set(sessionId, true);
|
|
105332
|
+
const activeSseCount = [...this.streamableHttpSseActive.values()].filter(Boolean).length;
|
|
105333
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] \u{1F4E1} GET SSE \u4EA4\u4ED8\u901A\u9053\u5DF2\u958B\u555F (sessionId=${sessionId})\uFF0C\u6D3B\u8E8D SSE \u6578: ${activeSseCount}`);
|
|
105334
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
105335
|
+
const heartbeat = setInterval(() => {
|
|
105300
105336
|
try {
|
|
105301
|
-
|
|
105302
|
-
|
|
105303
|
-
|
|
105304
|
-
|
|
105305
|
-
res.status(500).json({ error: "Failed to process request" });
|
|
105306
|
-
}
|
|
105337
|
+
if (!res.writableEnded) res.write(": ping\n\n");
|
|
105338
|
+
else clearInterval(heartbeat);
|
|
105339
|
+
} catch {
|
|
105340
|
+
clearInterval(heartbeat);
|
|
105307
105341
|
}
|
|
105308
|
-
}
|
|
105309
|
-
|
|
105342
|
+
}, 15e3);
|
|
105343
|
+
req.on("close", () => {
|
|
105344
|
+
clearInterval(heartbeat);
|
|
105345
|
+
this.streamableHttpSseActive.set(sessionId, false);
|
|
105346
|
+
const remaining = [...this.streamableHttpSseActive.values()].filter(Boolean).length;
|
|
105347
|
+
logger.warn(`[\u9023\u7DDA\u76E3\u63A7] \u26A0\uFE0F GET SSE \u4EA4\u4ED8\u901A\u9053\u5DF2\u95DC\u9589 (sessionId=${sessionId})\uFF0C\u5269\u9918\u6D3B\u8E8D SSE \u6578: ${remaining}`);
|
|
105348
|
+
});
|
|
105349
|
+
try {
|
|
105350
|
+
await transport.handleRequest(req, res);
|
|
105351
|
+
} catch (error2) {
|
|
105352
|
+
logger.error("\u8655\u7406 Streamable HTTP GET \u8ACB\u6C42\u5931\u6557:", error2);
|
|
105353
|
+
if (!res.headersSent) {
|
|
105354
|
+
res.status(500).json({ error: "Failed to process request" });
|
|
105355
|
+
}
|
|
105356
|
+
} finally {
|
|
105357
|
+
clearInterval(heartbeat);
|
|
105310
105358
|
}
|
|
105311
105359
|
});
|
|
105312
105360
|
logger.info("Streamable HTTP \u7AEF\u9EDE\u5DF2\u8A2D\u5B9A: GET/POST/DELETE /mcp");
|