🚌 fix: MCP Runtime Errors while Initializing (#9046)

* chore: Remove eslint-plugin-perfectionist from dependencies

* 🚌 fix: MCP Runtime Errors while Initializing

* chore: Bump @librechat/api version to 1.3.1

* chore: import order

* chore: import order
This commit is contained in:
Danny Avila 2025-08-13 14:41:38 -04:00 committed by GitHub
parent 3eb6debe6a
commit e6cebdf2b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 52 additions and 112 deletions

View file

@ -113,20 +113,26 @@ const getAvailableTools = async (req, res) => {
return; return;
} }
// If not in cache, build from manifest
let pluginManifest = availableTools; let pluginManifest = availableTools;
if (customConfig?.mcpServers != null) { if (customConfig?.mcpServers != null) {
const mcpManager = getMCPManager(); try {
const flowsCache = getLogStores(CacheKeys.FLOWS); const mcpManager = getMCPManager();
const flowManager = flowsCache ? getFlowStateManager(flowsCache) : null; const flowsCache = getLogStores(CacheKeys.FLOWS);
const serverToolsCallback = createServerToolsCallback(); const flowManager = flowsCache ? getFlowStateManager(flowsCache) : null;
const getServerTools = createGetServerTools(); const serverToolsCallback = createServerToolsCallback();
const mcpTools = await mcpManager.loadManifestTools({ const getServerTools = createGetServerTools();
flowManager, const mcpTools = await mcpManager.loadManifestTools({
serverToolsCallback, flowManager,
getServerTools, serverToolsCallback,
}); getServerTools,
pluginManifest = [...mcpTools, ...pluginManifest]; });
pluginManifest = [...mcpTools, ...pluginManifest];
} catch (error) {
logger.error(
'[getAvailableTools] Error loading MCP Tools, servers may still be initializing:',
error,
);
}
} }
/** @type {TPlugin[]} */ /** @type {TPlugin[]} */

View file

@ -109,10 +109,10 @@ router.get('/', async function (req, res) {
for (const serverName in config.mcpServers) { for (const serverName in config.mcpServers) {
const serverConfig = config.mcpServers[serverName]; const serverConfig = config.mcpServers[serverName];
payload.mcpServers[serverName] = { payload.mcpServers[serverName] = {
customUserVars: serverConfig?.customUserVars || {},
chatMenu: serverConfig?.chatMenu,
isOAuth: oauthServers.has(serverName),
startup: serverConfig?.startup, startup: serverConfig?.startup,
chatMenu: serverConfig?.chatMenu,
isOAuth: oauthServers?.has(serverName),
customUserVars: serverConfig?.customUserVars || {},
}; };
} }
} }

View file

@ -1,15 +1,21 @@
const { z } = require('zod'); const { z } = require('zod');
const { tool } = require('@langchain/core/tools'); const { tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas'); const { logger } = require('@librechat/data-schemas');
const { Time, CacheKeys, StepTypes } = require('librechat-data-provider');
const { Constants: AgentConstants, Providers, GraphEvents } = require('@librechat/agents'); const { Constants: AgentConstants, Providers, GraphEvents } = require('@librechat/agents');
const { Constants, ContentTypes, isAssistantsEndpoint } = require('librechat-data-provider');
const { const {
sendEvent, sendEvent,
MCPOAuthHandler, MCPOAuthHandler,
normalizeServerName, normalizeServerName,
convertWithResolvedRefs, convertWithResolvedRefs,
} = require('@librechat/api'); } = require('@librechat/api');
const {
Time,
CacheKeys,
StepTypes,
Constants,
ContentTypes,
isAssistantsEndpoint,
} = require('librechat-data-provider');
const { findToken, createToken, updateToken } = require('~/models'); const { findToken, createToken, updateToken } = require('~/models');
const { getMCPManager, getFlowStateManager } = require('~/config'); const { getMCPManager, getFlowStateManager } = require('~/config');
const { getCachedTools, loadCustomConfig } = require('./Config'); const { getCachedTools, loadCustomConfig } = require('./Config');
@ -254,15 +260,21 @@ async function getMCPSetupData(userId) {
} }
const mcpManager = getMCPManager(userId); const mcpManager = getMCPManager(userId);
const appConnections = mcpManager.getAllConnections() || new Map(); /** @type {ReturnType<MCPManager['getAllConnections']>} */
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 userConnections = mcpManager.getUserConnections(userId) || new Map();
const oauthServers = mcpManager.getOAuthServers() || new Set(); const oauthServers = mcpManager.getOAuthServers() || new Set();
return { return {
mcpConfig, mcpConfig,
oauthServers,
appConnections, appConnections,
userConnections, userConnections,
oauthServers,
}; };
} }

View file

@ -14,7 +14,7 @@ async function initializeMCPs(app) {
return; return;
} }
// Filter out servers with startup: false /** Servers filtered with `startup: false` */
const filteredServers = {}; const filteredServers = {};
for (const [name, config] of Object.entries(mcpServers)) { for (const [name, config] of Object.entries(mcpServers)) {
if (config.startup === false) { if (config.startup === false) {
@ -41,7 +41,7 @@ async function initializeMCPs(app) {
return; return;
} }
const mcpTools = mcpManager.getAppToolFunctions(); const mcpTools = mcpManager.getAppToolFunctions() ?? {};
await setCachedTools({ ...cachedTools, ...mcpTools }, { isGlobal: true }); await setCachedTools({ ...cachedTools, ...mcpTools }, { isGlobal: true });
const cache = getLogStores(CacheKeys.CONFIG_STORE); const cache = getLogStores(CacheKeys.CONFIG_STORE);

View file

@ -2,7 +2,6 @@ import { fileURLToPath } from 'node:url';
import path from 'node:path'; import path from 'node:path';
import typescriptEslintEslintPlugin from '@typescript-eslint/eslint-plugin'; import typescriptEslintEslintPlugin from '@typescript-eslint/eslint-plugin';
import { fixupConfigRules, fixupPluginRules } from '@eslint/compat'; import { fixupConfigRules, fixupPluginRules } from '@eslint/compat';
import perfectionist from 'eslint-plugin-perfectionist';
import reactHooks from 'eslint-plugin-react-hooks'; import reactHooks from 'eslint-plugin-react-hooks';
import tsParser from '@typescript-eslint/parser'; import tsParser from '@typescript-eslint/parser';
import importPlugin from 'eslint-plugin-import'; import importPlugin from 'eslint-plugin-import';
@ -62,7 +61,6 @@ export default [
'jsx-a11y': fixupPluginRules(jsxA11Y), 'jsx-a11y': fixupPluginRules(jsxA11Y),
'import/parsers': tsParser, 'import/parsers': tsParser,
i18next, i18next,
perfectionist,
prettier: fixupPluginRules(prettier), prettier: fixupPluginRules(prettier),
}, },
@ -139,46 +137,6 @@ export default [
'no-restricted-syntax': 'off', 'no-restricted-syntax': 'off',
'react/prop-types': 'off', 'react/prop-types': 'off',
'react/display-name': '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,
// },
// ],
}, },
}, },
{ {

31
package-lock.json generated
View file

@ -30,7 +30,6 @@
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^28.11.0", "eslint-plugin-jest": "^28.11.0",
"eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-perfectionist": "^4.8.0",
"eslint-plugin-prettier": "^5.2.3", "eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-react": "^7.37.4", "eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-hooks": "^5.1.0",
@ -33994,24 +33993,6 @@
"node": ">= 0.4" "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": { "node_modules/eslint-plugin-prettier": {
"version": "5.2.3", "version": "5.2.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz",
@ -41452,16 +41433,6 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true "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": { "node_modules/negotiator": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@ -51345,7 +51316,7 @@
}, },
"packages/api": { "packages/api": {
"name": "@librechat/api", "name": "@librechat/api",
"version": "1.3.0", "version": "1.3.1",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@babel/preset-env": "^7.21.5", "@babel/preset-env": "^7.21.5",

View file

@ -102,7 +102,6 @@
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^28.11.0", "eslint-plugin-jest": "^28.11.0",
"eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-perfectionist": "^4.8.0",
"eslint-plugin-prettier": "^5.2.3", "eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-react": "^7.37.4", "eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-hooks": "^5.1.0",

View file

@ -1,6 +1,6 @@
{ {
"name": "@librechat/api", "name": "@librechat/api",
"version": "1.3.0", "version": "1.3.1",
"type": "commonjs", "type": "commonjs",
"description": "MCP services for LibreChat", "description": "MCP services for LibreChat",
"main": "dist/index.js", "main": "dist/index.js",

View file

@ -5,10 +5,10 @@ import type { TUser } from 'librechat-data-provider';
import type { MCPOAuthTokens, MCPOAuthFlowMetadata } from '~/mcp/oauth'; import type { MCPOAuthTokens, MCPOAuthFlowMetadata } from '~/mcp/oauth';
import type { FlowStateManager } from '~/flow/manager'; import type { FlowStateManager } from '~/flow/manager';
import type { FlowMetadata } from '~/flow/types'; import type { FlowMetadata } from '~/flow/types';
import type * as t from './types';
import { MCPTokenStorage, MCPOAuthHandler } from '~/mcp/oauth'; import { MCPTokenStorage, MCPOAuthHandler } from '~/mcp/oauth';
import { MCPConnection } from './connection'; import { MCPConnection } from './connection';
import { processMCPEnv } from '~/utils'; import { processMCPEnv } from '~/utils';
import type * as t from './types';
export interface BasicConnectionOptions { export interface BasicConnectionOptions {
serverName: string; serverName: string;
@ -350,15 +350,9 @@ export class MCPConnectionFactory {
logger.info(`${this.logPrefix} OAuth flow started, issued authorization URL to user`); logger.info(`${this.logPrefix} OAuth flow started, issued authorization URL to user`);
await this.oauthStart(authorizationUrl); await this.oauthStart(authorizationUrl);
} else { } else {
logger.info(` logger.info(
`${this.logPrefix} OAuth flow started, no \`oauthStart\` handler defined, relying on callback endpoint`,
Please visit the following URL to authenticate: );
${authorizationUrl}
${this.logPrefix} Flow ID: ${newFlowId}
`);
} }
/** Tokens from the new flow */ /** Tokens from the new flow */

View file

@ -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 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 { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';
import type { TokenMethods } from '@librechat/data-schemas'; import type { TokenMethods } from '@librechat/data-schemas';
import type { TUser } from 'librechat-data-provider';
import type { FlowStateManager } from '~/flow/manager'; import type { FlowStateManager } from '~/flow/manager';
import type { TUser } from 'librechat-data-provider';
import type { MCPOAuthTokens } from '~/mcp/oauth'; import type { MCPOAuthTokens } from '~/mcp/oauth';
import type * as t from './types';
import { UserConnectionManager } from '~/mcp/UserConnectionManager'; import { UserConnectionManager } from '~/mcp/UserConnectionManager';
import { ConnectionsRepository } from '~/mcp/ConnectionsRepository'; import { ConnectionsRepository } from '~/mcp/ConnectionsRepository';
import { formatToolContent } from './parsers'; import { formatToolContent } from './parsers';
import { MCPConnection } from './connection'; import { MCPConnection } from './connection';
import { CONSTANTS } from './enum'; import { CONSTANTS } from './enum';
import type * as t from './types';
/** /**
* Centralized manager for MCP server connections and tool execution. * Centralized manager for MCP server connections and tool execution.
@ -43,17 +43,17 @@ export class MCPManager extends UserConnectionManager {
} }
/** Returns all app-level connections */ /** Returns all app-level connections */
public async getAllConnections(): Promise<Map<string, MCPConnection>> { public async getAllConnections(): Promise<Map<string, MCPConnection> | null> {
return this.appConnections!.getAll(); return this.appConnections!.getAll();
} }
/** Get servers that require OAuth */ /** Get servers that require OAuth */
public getOAuthServers(): Set<string> { public getOAuthServers(): Set<string> | null {
return this.serversRegistry.oauthServers!; return this.serversRegistry.oauthServers!;
} }
/** Returns all available tool functions from app-level connections */ /** Returns all available tool functions from app-level connections */
public getAppToolFunctions(): t.LCAvailableTools { public getAppToolFunctions(): t.LCAvailableTools | null {
return this.serversRegistry.toolFunctions!; return this.serversRegistry.toolFunctions!;
} }