LibreChat/api/server/services/MCPRequestContext.js
Danny Avila 139d61c437
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Waiting to run
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions
Sync Helm Chart Tags / Ignore non-main push (push) Waiting to run
Sync Helm Chart Tags / Sync chart tags (push) Waiting to run
🚐 fix: Reuse Request-Scoped MCP Connections per Run (#13673)
* fix(mcp): reuse request-scoped connections per run

* test(mcp): update connection factory defaults
2026-06-11 01:17:14 -04:00

69 lines
1.7 KiB
JavaScript

const { logger } = require('@librechat/data-schemas');
const MCP_REQUEST_CONTEXT = Symbol.for('librechat.mcpRequestContext');
function createMCPRequestContext() {
return {
connections: new Map(),
pending: new Map(),
cleanupStarted: false,
};
}
async function cleanupMCPRequestContext(context) {
if (!context || context.cleanupStarted) {
return;
}
context.cleanupStarted = true;
const connections = new Set(context.connections.values());
const pending = Array.from(context.pending.values());
if (pending.length > 0) {
const settled = await Promise.allSettled(pending);
for (const result of settled) {
if (result.status === 'fulfilled' && result.value) {
connections.add(result.value);
}
}
}
await Promise.allSettled(
Array.from(connections).map(async (connection) => {
try {
await connection.disconnect();
} catch (error) {
logger.warn('[MCP Request Context] Failed to disconnect request-scoped connection', error);
}
}),
);
context.connections.clear();
context.pending.clear();
}
function getMCPRequestContext(req, res) {
if (!req) {
return undefined;
}
if (!req[MCP_REQUEST_CONTEXT]) {
const context = createMCPRequestContext();
req[MCP_REQUEST_CONTEXT] = context;
const cleanup = () => {
cleanupMCPRequestContext(context).catch((error) => {
logger.warn('[MCP Request Context] Cleanup failed', error);
});
};
res?.once?.('finish', cleanup);
res?.once?.('close', cleanup);
}
return req[MCP_REQUEST_CONTEXT];
}
module.exports = {
cleanupMCPRequestContext,
createMCPRequestContext,
getMCPRequestContext,
};