mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 06:00:56 +02:00
🗝️ 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>
This commit is contained in:
parent
8b15bb2ed6
commit
3e4b01de82
36 changed files with 1536 additions and 166 deletions
|
@ -5,6 +5,7 @@ import { createRoleMethods, type RoleMethods } from './role';
|
|||
/* Memories */
|
||||
import { createMemoryMethods, type MemoryMethods } from './memory';
|
||||
import { createShareMethods, type ShareMethods } from './share';
|
||||
import { createPluginAuthMethods, type PluginAuthMethods } from './pluginAuth';
|
||||
|
||||
/**
|
||||
* Creates all database methods for all collections
|
||||
|
@ -17,13 +18,15 @@ export function createMethods(mongoose: typeof import('mongoose')) {
|
|||
...createRoleMethods(mongoose),
|
||||
...createMemoryMethods(mongoose),
|
||||
...createShareMethods(mongoose),
|
||||
...createPluginAuthMethods(mongoose),
|
||||
};
|
||||
}
|
||||
|
||||
export type { MemoryMethods, ShareMethods, TokenMethods };
|
||||
export type { MemoryMethods, ShareMethods, TokenMethods, PluginAuthMethods };
|
||||
export type AllMethods = UserMethods &
|
||||
SessionMethods &
|
||||
TokenMethods &
|
||||
RoleMethods &
|
||||
MemoryMethods &
|
||||
ShareMethods;
|
||||
ShareMethods &
|
||||
PluginAuthMethods;
|
||||
|
|
140
packages/data-schemas/src/methods/pluginAuth.ts
Normal file
140
packages/data-schemas/src/methods/pluginAuth.ts
Normal file
|
@ -0,0 +1,140 @@
|
|||
import type { DeleteResult, Model } from 'mongoose';
|
||||
import type { IPluginAuth } from '~/schema/pluginAuth';
|
||||
import type {
|
||||
FindPluginAuthsByKeysParams,
|
||||
UpdatePluginAuthParams,
|
||||
DeletePluginAuthParams,
|
||||
FindPluginAuthParams,
|
||||
} from '~/types';
|
||||
|
||||
// Factory function that takes mongoose instance and returns the methods
|
||||
export function createPluginAuthMethods(mongoose: typeof import('mongoose')) {
|
||||
const PluginAuth: Model<IPluginAuth> = mongoose.models.PluginAuth;
|
||||
|
||||
/**
|
||||
* Finds a single plugin auth entry by userId and authField
|
||||
*/
|
||||
async function findOnePluginAuth({
|
||||
userId,
|
||||
authField,
|
||||
}: FindPluginAuthParams): Promise<IPluginAuth | null> {
|
||||
try {
|
||||
return await PluginAuth.findOne({ userId, authField }).lean();
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to find plugin auth: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds multiple plugin auth entries by userId and pluginKeys
|
||||
*/
|
||||
async function findPluginAuthsByKeys({
|
||||
userId,
|
||||
pluginKeys,
|
||||
}: FindPluginAuthsByKeysParams): Promise<IPluginAuth[]> {
|
||||
try {
|
||||
if (!pluginKeys || pluginKeys.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return await PluginAuth.find({
|
||||
userId,
|
||||
pluginKey: { $in: pluginKeys },
|
||||
}).lean();
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to find plugin auths: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates or creates a plugin auth entry
|
||||
*/
|
||||
async function updatePluginAuth({
|
||||
userId,
|
||||
authField,
|
||||
pluginKey,
|
||||
value,
|
||||
}: UpdatePluginAuthParams): Promise<IPluginAuth> {
|
||||
try {
|
||||
const existingAuth = await PluginAuth.findOne({ userId, pluginKey, authField }).lean();
|
||||
|
||||
if (existingAuth) {
|
||||
return await PluginAuth.findOneAndUpdate(
|
||||
{ userId, pluginKey, authField },
|
||||
{ $set: { value } },
|
||||
{ new: true, upsert: true },
|
||||
).lean();
|
||||
} else {
|
||||
const newPluginAuth = await new PluginAuth({
|
||||
userId,
|
||||
authField,
|
||||
value,
|
||||
pluginKey,
|
||||
});
|
||||
await newPluginAuth.save();
|
||||
return newPluginAuth.toObject();
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to update plugin auth: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes plugin auth entries based on provided parameters
|
||||
*/
|
||||
async function deletePluginAuth({
|
||||
userId,
|
||||
authField,
|
||||
pluginKey,
|
||||
all = false,
|
||||
}: DeletePluginAuthParams): Promise<DeleteResult> {
|
||||
try {
|
||||
if (all) {
|
||||
const filter: DeletePluginAuthParams = { userId };
|
||||
if (pluginKey) {
|
||||
filter.pluginKey = pluginKey;
|
||||
}
|
||||
return await PluginAuth.deleteMany(filter);
|
||||
}
|
||||
|
||||
if (!authField) {
|
||||
throw new Error('authField is required when all is false');
|
||||
}
|
||||
|
||||
return await PluginAuth.deleteOne({ userId, authField });
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to delete plugin auth: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all plugin auth entries for a user
|
||||
*/
|
||||
async function deleteAllUserPluginAuths(userId: string): Promise<DeleteResult> {
|
||||
try {
|
||||
return await PluginAuth.deleteMany({ userId });
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to delete all user plugin auths: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
findOnePluginAuth,
|
||||
findPluginAuthsByKeys,
|
||||
updatePluginAuth,
|
||||
deletePluginAuth,
|
||||
deleteAllUserPluginAuths,
|
||||
};
|
||||
}
|
||||
|
||||
export type PluginAuthMethods = ReturnType<typeof createPluginAuthMethods>;
|
|
@ -1,13 +1,5 @@
|
|||
import { Schema, Document } from 'mongoose';
|
||||
|
||||
export interface IPluginAuth extends Document {
|
||||
authField: string;
|
||||
value: string;
|
||||
userId: string;
|
||||
pluginKey?: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
import { Schema } from 'mongoose';
|
||||
import type { IPluginAuth } from '~/types';
|
||||
|
||||
const pluginAuthSchema: Schema<IPluginAuth> = new Schema(
|
||||
{
|
||||
|
|
|
@ -14,5 +14,6 @@ export * from './action';
|
|||
export * from './assistant';
|
||||
export * from './file';
|
||||
export * from './share';
|
||||
export * from './pluginAuth';
|
||||
/* Memories */
|
||||
export * from './memory';
|
||||
|
|
40
packages/data-schemas/src/types/pluginAuth.ts
Normal file
40
packages/data-schemas/src/types/pluginAuth.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import type { Document } from 'mongoose';
|
||||
|
||||
export interface IPluginAuth extends Document {
|
||||
authField: string;
|
||||
value: string;
|
||||
userId: string;
|
||||
pluginKey?: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export interface PluginAuthQuery {
|
||||
userId: string;
|
||||
authField?: string;
|
||||
pluginKey?: string;
|
||||
}
|
||||
|
||||
export interface FindPluginAuthParams {
|
||||
userId: string;
|
||||
authField: string;
|
||||
}
|
||||
|
||||
export interface FindPluginAuthsByKeysParams {
|
||||
userId: string;
|
||||
pluginKeys: string[];
|
||||
}
|
||||
|
||||
export interface UpdatePluginAuthParams {
|
||||
userId: string;
|
||||
authField: string;
|
||||
pluginKey: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface DeletePluginAuthParams {
|
||||
userId: string;
|
||||
authField?: string;
|
||||
pluginKey?: string;
|
||||
all?: boolean;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue