LibreChat/api/server/services/Config/getCustomConfig.js
Danny Avila 3e4b01de82
🗝️ feat: User Provided Credentials for MCP Servers (#7980)
* 🗝️ feat: Per-User Credentials for MCP Servers

chore: add aider to gitignore

feat: fill custom variables to MCP server

feat: replace placeholders with custom user MCP variables

feat: handle MCP install/uninstall (uses pluginauths)

feat: add MCP custom variables dialog to MCPSelect

feat: add MCP custom variables dialog to the side panel

feat: do not require to fill MCP credentials for in tools dialog

feat: add translations keys (en+cs) for custom MCP variables

fix: handle LIBRECHAT_USER_ID correctly during MCP var replacement

style: remove unused MCP translation keys

style: fix eslint for MCP custom vars

chore: move aider gitignore to AI section

* feat: Add Plugin Authentication Methods to data-schemas

* refactor: Replace PluginAuth model methods with new utility functions for improved code organization and maintainability

* refactor: Move IPluginAuth interface to types directory for better organization and update pluginAuth schema to use the new import

* refactor: Remove unused getUsersPluginsAuthValuesMap function and streamline PluginService.js; add new getPluginAuthMap function for improved plugin authentication handling

* chore: fix typing for optional tools property with GenericTool[] type

* chore: update librechat-data-provider version to 0.7.88

* refactor: optimize getUserMCPAuthMap function by reducing variable usage and improving server key collection logic

* refactor: streamline MCP tool creation by removing customUserVars parameter and enhancing user-specific authentication handling to avoid closure encapsulation

* refactor: extract processSingleValue function to streamline MCP environment variable processing and enhance readability

* refactor: enhance MCP tool processing logic by simplifying conditions and improving authentication handling for custom user variables

* ci: fix action tests

* chore: fix imports, remove comments

* chore: remove non-english translations

* fix: remove newline at end of translation.json file

---------

Co-authored-by: Aleš Kůtek <kutekales@gmail.com>
2025-06-19 18:27:55 -04:00

99 lines
2.9 KiB
JavaScript

const { logger } = require('@librechat/data-schemas');
const { getUserMCPAuthMap } = require('@librechat/api');
const { CacheKeys, EModelEndpoint } = require('librechat-data-provider');
const { normalizeEndpointName, isEnabled } = require('~/server/utils');
const loadCustomConfig = require('./loadCustomConfig');
const { getCachedTools } = require('./getCachedTools');
const { findPluginAuthsByKeys } = require('~/models');
const getLogStores = require('~/cache/getLogStores');
/**
* Retrieves the configuration object
* @function getCustomConfig
* @returns {Promise<TCustomConfig | null>}
* */
async function getCustomConfig() {
const cache = getLogStores(CacheKeys.CONFIG_STORE);
return (await cache.get(CacheKeys.CUSTOM_CONFIG)) || (await loadCustomConfig());
}
/**
* Retrieves the configuration object
* @function getBalanceConfig
* @returns {Promise<TCustomConfig['balance'] | null>}
* */
async function getBalanceConfig() {
const isLegacyEnabled = isEnabled(process.env.CHECK_BALANCE);
const startBalance = process.env.START_BALANCE;
/** @type {TCustomConfig['balance']} */
const config = {
enabled: isLegacyEnabled,
startBalance: startBalance != null && startBalance ? parseInt(startBalance, 10) : undefined,
};
const customConfig = await getCustomConfig();
if (!customConfig) {
return config;
}
return { ...config, ...(customConfig?.['balance'] ?? {}) };
}
/**
*
* @param {string | EModelEndpoint} endpoint
*/
const getCustomEndpointConfig = async (endpoint) => {
const customConfig = await getCustomConfig();
if (!customConfig) {
throw new Error(`Config not found for the ${endpoint} custom endpoint.`);
}
const { endpoints = {} } = customConfig;
const customEndpoints = endpoints[EModelEndpoint.custom] ?? [];
return customEndpoints.find(
(endpointConfig) => normalizeEndpointName(endpointConfig.name) === endpoint,
);
};
async function createGetMCPAuthMap() {
const customConfig = await getCustomConfig();
const mcpServers = customConfig?.mcpServers;
const hasCustomUserVars = Object.values(mcpServers).some((server) => server.customUserVars);
if (!hasCustomUserVars) {
return;
}
/**
* @param {Object} params
* @param {GenericTool[]} [params.tools]
* @param {string} params.userId
* @returns {Promise<Record<string, Record<string, string>> | undefined>}
*/
return async function ({ tools, userId }) {
try {
if (!tools || tools.length === 0) {
return;
}
const appTools = await getCachedTools({
userId,
});
return await getUserMCPAuthMap({
tools,
userId,
appTools,
findPluginAuthsByKeys,
});
} catch (err) {
logger.error(
`[api/server/controllers/agents/client.js #chatCompletion] Error getting custom user vars for agent`,
err,
);
}
};
}
module.exports = {
getCustomConfig,
getBalanceConfig,
createGetMCPAuthMap,
getCustomEndpointConfig,
};