🧰 refactor: Decouple MCP Tools from System Tools (#9748)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run

This commit is contained in:
Danny Avila 2025-09-21 07:56:40 -04:00 committed by GitHub
parent 9d2aba5df5
commit 386900fb4f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 1032 additions and 1195 deletions

View file

@ -1,32 +1,37 @@
import { useMemo } from 'react';
import { Constants, EModelEndpoint } from 'librechat-data-provider';
import { Constants } from 'librechat-data-provider';
import type { TPlugin } from 'librechat-data-provider';
import { useAvailableToolsQuery, useGetStartupConfig } from '~/data-provider';
import { useMCPToolsQuery, useGetStartupConfig } from '~/data-provider';
/**
* Hook for fetching and filtering MCP tools based on server configuration
* Uses the dedicated MCP tools query instead of filtering from general tools
*/
export function useGetMCPTools() {
const { data: startupConfig } = useGetStartupConfig();
const { data: rawMcpTools } = useAvailableToolsQuery(EModelEndpoint.agents, {
// Use dedicated MCP tools query
const { data: rawMcpTools } = useMCPToolsQuery({
select: (data: TPlugin[]) => {
// Group tools by server for easier management
const mcpToolsMap = new Map<string, TPlugin>();
data.forEach((tool) => {
const isMCP = tool.pluginKey.includes(Constants.mcp_delimiter);
if (isMCP) {
const parts = tool.pluginKey.split(Constants.mcp_delimiter);
const serverName = parts[parts.length - 1];
if (!mcpToolsMap.has(serverName)) {
mcpToolsMap.set(serverName, {
name: serverName,
pluginKey: tool.pluginKey,
authConfig: tool.authConfig,
authenticated: tool.authenticated,
});
}
const parts = tool.pluginKey.split(Constants.mcp_delimiter);
const serverName = parts[parts.length - 1];
if (!mcpToolsMap.has(serverName)) {
mcpToolsMap.set(serverName, {
name: serverName,
pluginKey: tool.pluginKey,
authConfig: tool.authConfig,
authenticated: tool.authenticated,
});
}
});
return Array.from(mcpToolsMap.values());
},
});
// Filter out servers that have chatMenu disabled
const mcpToolDetails = useMemo(() => {
if (!rawMcpTools || !startupConfig?.mcpServers) {
return rawMcpTools;

View file

@ -53,7 +53,7 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
showToast({ message: localize('com_nav_mcp_vars_updated'), status: 'success' });
await Promise.all([
queryClient.refetchQueries([QueryKeys.tools]),
queryClient.refetchQueries([QueryKeys.mcpTools]),
queryClient.refetchQueries([QueryKeys.mcpAuthValues]),
queryClient.refetchQueries([QueryKeys.mcpConnectionStatus]),
]);
@ -170,7 +170,7 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
setMCPValues([...currentValues, serverName]);
}
await queryClient.invalidateQueries([QueryKeys.tools]);
await queryClient.invalidateQueries([QueryKeys.mcpTools]);
// This delay is to ensure UI has updated with new connection status before cleanup
// Otherwise servers will show as disconnected for a second after OAuth flow completes

View file

@ -1,79 +1,52 @@
import { useMemo } from 'react';
import { Constants } from 'librechat-data-provider';
import type { AgentToolType } from 'librechat-data-provider';
import type { TPlugin } from 'librechat-data-provider';
import type { MCPServerInfo } from '~/common';
type GroupedToolType = AgentToolType & { tools?: AgentToolType[] };
type GroupedToolsRecord = Record<string, GroupedToolType>;
interface VisibleToolsResult {
toolIds: string[];
mcpServerNames: string[];
}
/**
* Custom hook to calculate visible tool IDs based on selected tools and their parent groups.
* If any subtool of a group is selected, the parent group tool is also made visible.
* Custom hook to calculate visible tool IDs based on selected tools.
* Separates regular LibreChat tools from MCP servers.
*
* @param selectedToolIds - Array of selected tool IDs
* @param allTools - Record of all available tools
* @param regularTools - Array of regular LibreChat tools
* @param mcpServersMap - Map of all MCP servers
* @returns Object containing separate arrays of visible tool IDs for regular and MCP tools
*/
export function useVisibleTools(
selectedToolIds: string[] | undefined,
allTools: GroupedToolsRecord | undefined,
regularTools: TPlugin[] | undefined,
mcpServersMap: Map<string, MCPServerInfo>,
): VisibleToolsResult {
return useMemo(() => {
const mcpServers = new Set<string>();
const selectedSet = new Set<string>();
const regularToolIds = new Set<string>();
const regularToolIds: string[] = [];
for (const toolId of selectedToolIds ?? []) {
if (!toolId.includes(Constants.mcp_delimiter)) {
selectedSet.add(toolId);
continue;
}
const serverName = toolId.split(Constants.mcp_delimiter)[1];
if (!serverName) {
continue;
}
mcpServers.add(serverName);
}
if (allTools) {
for (const [toolId, toolObj] of Object.entries(allTools)) {
if (selectedSet.has(toolId)) {
regularToolIds.add(toolId);
}
if (toolObj.tools?.length) {
for (const subtool of toolObj.tools) {
if (selectedSet.has(subtool.tool_id)) {
regularToolIds.add(toolId);
break;
}
}
// MCP tools/servers
if (toolId.includes(Constants.mcp_delimiter)) {
const serverName = toolId.split(Constants.mcp_delimiter)[1];
if (serverName) {
mcpServers.add(serverName);
}
}
}
if (mcpServersMap) {
for (const [mcpServerName] of mcpServersMap) {
if (mcpServers.has(mcpServerName)) {
continue;
}
/** Legacy check */
if (selectedSet.has(mcpServerName)) {
mcpServers.add(mcpServerName);
}
// Legacy MCP server check (just server name)
else if (mcpServersMap.has(toolId)) {
mcpServers.add(toolId);
}
// Regular LibreChat tools
else if (regularTools?.some((t) => t.pluginKey === toolId)) {
regularToolIds.push(toolId);
}
}
return {
toolIds: Array.from(regularToolIds).sort((a, b) => a.localeCompare(b)),
toolIds: regularToolIds.sort((a, b) => a.localeCompare(b)),
mcpServerNames: Array.from(mcpServers).sort((a, b) => a.localeCompare(b)),
};
}, [allTools, mcpServersMap, selectedToolIds]);
}, [regularTools, mcpServersMap, selectedToolIds]);
}