🚀 feat: Enhance MCP Connections For Multi-User Support (#6610)

* feat: first pass, multi-user connections

* 🔧 refactor: Enhance MCPConnection logging with user-specific prefixes

* 🔧 chore: Update @modelcontextprotocol/sdk dependency to version 1.8.0

* feat: idle timeout for user mcp connections

* chore: increase user connection idle timeout to 15 minutes

* feat: implement graceful shutdown for MCP servers on termination signal

* feat: implement user idle timeout management and last activity tracking

* feat: enhance MCP options to support custom headers and user ID in environment variable processing

* feat: update user last activity tracking in MCPManager

* refactor: remove default OpenRouter completions URL from OpenAIClient

* refactor: simplify log messages by removing redundant 'App' prefix in MCPManager

* refactor: show Agents Builder even if not using Agents endpoint

* refactor: remove redundant 'App' prefix from disconnect error log messages in MCPManager

* refactor: remove 'App' prefix from log prefix in MCPConnection

* chore: remove unecessary comment

* fix: allow error propagation during MCPManager initialization
This commit is contained in:
Danny Avila 2025-03-28 15:21:10 -04:00 committed by GitHub
parent e630c0a00d
commit a10bc87979
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 667 additions and 430 deletions

View file

@ -65,6 +65,7 @@ export const WebSocketOptionsSchema = BaseOptionsSchema.extend({
export const SSEOptionsSchema = BaseOptionsSchema.extend({
type: z.literal('sse').optional(),
headers: z.record(z.string(), z.string()).optional(),
url: z
.string()
.url()
@ -92,9 +93,10 @@ export type MCPOptions = z.infer<typeof MCPOptionsSchema>;
/**
* Recursively processes an object to replace environment variables in string values
* @param {MCPOptions} obj - The object to process
* @param {string} [userId] - The user ID
* @returns {MCPOptions} - The processed object with environment variables replaced
*/
export function processMCPEnv(obj: MCPOptions): MCPOptions {
export function processMCPEnv(obj: MCPOptions, userId?: string): MCPOptions {
if (obj === null || obj === undefined) {
return obj;
}
@ -105,6 +107,16 @@ export function processMCPEnv(obj: MCPOptions): MCPOptions {
processedEnv[key] = extractEnvVariable(value);
}
obj.env = processedEnv;
} else if ('headers' in obj && obj.headers) {
const processedHeaders: Record<string, string> = {};
for (const [key, value] of Object.entries(obj.headers)) {
if (value === '{{LIBRECHAT_USER_ID}}' && userId != null && userId) {
processedHeaders[key] = userId;
continue;
}
processedHeaders[key] = extractEnvVariable(value);
}
obj.headers = processedHeaders;
}
return obj;