diff --git a/api/server/controllers/PluginController.js b/api/server/controllers/PluginController.js index 9be4b53e3..06b4585a0 100644 --- a/api/server/controllers/PluginController.js +++ b/api/server/controllers/PluginController.js @@ -113,20 +113,26 @@ const getAvailableTools = async (req, res) => { return; } - // If not in cache, build from manifest let pluginManifest = availableTools; if (customConfig?.mcpServers != null) { - const mcpManager = getMCPManager(); - const flowsCache = getLogStores(CacheKeys.FLOWS); - const flowManager = flowsCache ? getFlowStateManager(flowsCache) : null; - const serverToolsCallback = createServerToolsCallback(); - const getServerTools = createGetServerTools(); - const mcpTools = await mcpManager.loadManifestTools({ - flowManager, - serverToolsCallback, - getServerTools, - }); - pluginManifest = [...mcpTools, ...pluginManifest]; + try { + const mcpManager = getMCPManager(); + const flowsCache = getLogStores(CacheKeys.FLOWS); + const flowManager = flowsCache ? getFlowStateManager(flowsCache) : null; + const serverToolsCallback = createServerToolsCallback(); + const getServerTools = createGetServerTools(); + const mcpTools = await mcpManager.loadManifestTools({ + flowManager, + serverToolsCallback, + getServerTools, + }); + pluginManifest = [...mcpTools, ...pluginManifest]; + } catch (error) { + logger.error( + '[getAvailableTools] Error loading MCP Tools, servers may still be initializing:', + error, + ); + } } /** @type {TPlugin[]} */ diff --git a/api/server/routes/config.js b/api/server/routes/config.js index bd1b0b12c..84cef4497 100644 --- a/api/server/routes/config.js +++ b/api/server/routes/config.js @@ -109,10 +109,10 @@ router.get('/', async function (req, res) { for (const serverName in config.mcpServers) { const serverConfig = config.mcpServers[serverName]; payload.mcpServers[serverName] = { - customUserVars: serverConfig?.customUserVars || {}, - chatMenu: serverConfig?.chatMenu, - isOAuth: oauthServers.has(serverName), startup: serverConfig?.startup, + chatMenu: serverConfig?.chatMenu, + isOAuth: oauthServers?.has(serverName), + customUserVars: serverConfig?.customUserVars || {}, }; } } diff --git a/api/server/services/MCP.js b/api/server/services/MCP.js index 147def1bb..586c50c09 100644 --- a/api/server/services/MCP.js +++ b/api/server/services/MCP.js @@ -1,15 +1,21 @@ const { z } = require('zod'); const { tool } = require('@langchain/core/tools'); const { logger } = require('@librechat/data-schemas'); -const { Time, CacheKeys, StepTypes } = require('librechat-data-provider'); const { Constants: AgentConstants, Providers, GraphEvents } = require('@librechat/agents'); -const { Constants, ContentTypes, isAssistantsEndpoint } = require('librechat-data-provider'); const { sendEvent, MCPOAuthHandler, normalizeServerName, convertWithResolvedRefs, } = require('@librechat/api'); +const { + Time, + CacheKeys, + StepTypes, + Constants, + ContentTypes, + isAssistantsEndpoint, +} = require('librechat-data-provider'); const { findToken, createToken, updateToken } = require('~/models'); const { getMCPManager, getFlowStateManager } = require('~/config'); const { getCachedTools, loadCustomConfig } = require('./Config'); @@ -254,15 +260,21 @@ async function getMCPSetupData(userId) { } const mcpManager = getMCPManager(userId); - const appConnections = mcpManager.getAllConnections() || new Map(); + /** @type {ReturnType} */ + let appConnections = new Map(); + try { + appConnections = (await mcpManager.getAllConnections()) || new Map(); + } catch (error) { + logger.error(`[MCP][User: ${userId}] Error getting app connections:`, error); + } const userConnections = mcpManager.getUserConnections(userId) || new Map(); const oauthServers = mcpManager.getOAuthServers() || new Set(); return { mcpConfig, + oauthServers, appConnections, userConnections, - oauthServers, }; } diff --git a/api/server/services/initializeMCPs.js b/api/server/services/initializeMCPs.js index 40c75e1b0..2293116ff 100644 --- a/api/server/services/initializeMCPs.js +++ b/api/server/services/initializeMCPs.js @@ -14,7 +14,7 @@ async function initializeMCPs(app) { return; } - // Filter out servers with startup: false + /** Servers filtered with `startup: false` */ const filteredServers = {}; for (const [name, config] of Object.entries(mcpServers)) { if (config.startup === false) { @@ -41,7 +41,7 @@ async function initializeMCPs(app) { return; } - const mcpTools = mcpManager.getAppToolFunctions(); + const mcpTools = mcpManager.getAppToolFunctions() ?? {}; await setCachedTools({ ...cachedTools, ...mcpTools }, { isGlobal: true }); const cache = getLogStores(CacheKeys.CONFIG_STORE); diff --git a/eslint.config.mjs b/eslint.config.mjs index f53e8cc69..f84a57058 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -2,7 +2,6 @@ import { fileURLToPath } from 'node:url'; import path from 'node:path'; import typescriptEslintEslintPlugin from '@typescript-eslint/eslint-plugin'; import { fixupConfigRules, fixupPluginRules } from '@eslint/compat'; -import perfectionist from 'eslint-plugin-perfectionist'; import reactHooks from 'eslint-plugin-react-hooks'; import tsParser from '@typescript-eslint/parser'; import importPlugin from 'eslint-plugin-import'; @@ -62,7 +61,6 @@ export default [ 'jsx-a11y': fixupPluginRules(jsxA11Y), 'import/parsers': tsParser, i18next, - perfectionist, prettier: fixupPluginRules(prettier), }, @@ -139,46 +137,6 @@ export default [ 'no-restricted-syntax': 'off', 'react/prop-types': 'off', 'react/display-name': 'off', - - 'perfectionist/sort-imports': [ - 'error', - { - type: 'line-length', - order: 'desc', - newlinesBetween: 'never', - customGroups: { - value: { - react: ['^react$'], - local: ['^(\\.{1,2}|~)/', '^librechat-data-provider'], - }, - }, - groups: [ - 'react', - 'builtin', - 'external', - ['builtin-type', 'external-type'], - ['internal-type'], - 'local', - ['parent', 'sibling', 'index'], - 'object', - 'unknown', - ], - }, - ], - - // 'perfectionist/sort-named-imports': [ - // 'error', - // { - // type: 'line-length', - // order: 'asc', - // ignoreAlias: false, - // ignoreCase: true, - // specialCharacters: 'keep', - // groupKind: 'mixed', - // partitionByNewLine: false, - // partitionByComment: false, - // }, - // ], }, }, { diff --git a/package-lock.json b/package-lock.json index c851b3650..49bb7cd01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,6 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-perfectionist": "^4.8.0", "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.1.0", @@ -33994,24 +33993,6 @@ "node": ">= 0.4" } }, - "node_modules/eslint-plugin-perfectionist": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-4.8.0.tgz", - "integrity": "sha512-ZF04IAPGItYMlj9xjgvvl/QpksZf79g0dkxbNcuxDjbcUSZ4CwucJ7h5Yzt5JuHe+i6igQbUYEp40j4ndfbvWQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "^8.23.0", - "@typescript-eslint/utils": "^8.23.0", - "natural-orderby": "^5.0.0" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "eslint": ">=8.0.0" - } - }, "node_modules/eslint-plugin-prettier": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", @@ -41452,16 +41433,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/natural-orderby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-5.0.0.tgz", - "integrity": "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -51345,7 +51316,7 @@ }, "packages/api": { "name": "@librechat/api", - "version": "1.3.0", + "version": "1.3.1", "license": "ISC", "devDependencies": { "@babel/preset-env": "^7.21.5", diff --git a/package.json b/package.json index 925f09850..589f6494c 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,6 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-perfectionist": "^4.8.0", "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.1.0", diff --git a/packages/api/package.json b/packages/api/package.json index 8d4acd3ae..95bb63a00 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@librechat/api", - "version": "1.3.0", + "version": "1.3.1", "type": "commonjs", "description": "MCP services for LibreChat", "main": "dist/index.js", diff --git a/packages/api/src/mcp/MCPConnectionFactory.ts b/packages/api/src/mcp/MCPConnectionFactory.ts index f657f0255..e110ee716 100644 --- a/packages/api/src/mcp/MCPConnectionFactory.ts +++ b/packages/api/src/mcp/MCPConnectionFactory.ts @@ -5,10 +5,10 @@ import type { TUser } from 'librechat-data-provider'; import type { MCPOAuthTokens, MCPOAuthFlowMetadata } from '~/mcp/oauth'; import type { FlowStateManager } from '~/flow/manager'; import type { FlowMetadata } from '~/flow/types'; +import type * as t from './types'; import { MCPTokenStorage, MCPOAuthHandler } from '~/mcp/oauth'; import { MCPConnection } from './connection'; import { processMCPEnv } from '~/utils'; -import type * as t from './types'; export interface BasicConnectionOptions { serverName: string; @@ -350,15 +350,9 @@ export class MCPConnectionFactory { logger.info(`${this.logPrefix} OAuth flow started, issued authorization URL to user`); await this.oauthStart(authorizationUrl); } else { - logger.info(` -═══════════════════════════════════════════════════════════════════════ -Please visit the following URL to authenticate: - -${authorizationUrl} - -${this.logPrefix} Flow ID: ${newFlowId} -═══════════════════════════════════════════════════════════════════════ -`); + logger.info( + `${this.logPrefix} OAuth flow started, no \`oauthStart\` handler defined, relying on callback endpoint`, + ); } /** Tokens from the new flow */ diff --git a/packages/api/src/mcp/MCPManager.ts b/packages/api/src/mcp/MCPManager.ts index 9173ec58a..b201df46c 100644 --- a/packages/api/src/mcp/MCPManager.ts +++ b/packages/api/src/mcp/MCPManager.ts @@ -1,17 +1,17 @@ -import { CallToolResultSchema, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; -import { logger } from '@librechat/data-schemas'; import pick from 'lodash/pick'; +import { logger } from '@librechat/data-schemas'; +import { CallToolResultSchema, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; import type { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js'; import type { TokenMethods } from '@librechat/data-schemas'; -import type { TUser } from 'librechat-data-provider'; import type { FlowStateManager } from '~/flow/manager'; +import type { TUser } from 'librechat-data-provider'; import type { MCPOAuthTokens } from '~/mcp/oauth'; +import type * as t from './types'; import { UserConnectionManager } from '~/mcp/UserConnectionManager'; import { ConnectionsRepository } from '~/mcp/ConnectionsRepository'; import { formatToolContent } from './parsers'; import { MCPConnection } from './connection'; import { CONSTANTS } from './enum'; -import type * as t from './types'; /** * Centralized manager for MCP server connections and tool execution. @@ -43,17 +43,17 @@ export class MCPManager extends UserConnectionManager { } /** Returns all app-level connections */ - public async getAllConnections(): Promise> { + public async getAllConnections(): Promise | null> { return this.appConnections!.getAll(); } /** Get servers that require OAuth */ - public getOAuthServers(): Set { + public getOAuthServers(): Set | null { return this.serversRegistry.oauthServers!; } /** Returns all available tool functions from app-level connections */ - public getAppToolFunctions(): t.LCAvailableTools { + public getAppToolFunctions(): t.LCAvailableTools | null { return this.serversRegistry.toolFunctions!; }