👻 refactor: LocalStorage Cleanup and MCP State Optimization (#9528)

* 👻 refactor: MCP Select State with Jotai Atoms

* refactor: Implement timestamp management for ChatArea localStorage entries

* refactor: Integrate MCP Server Manager into BadgeRow context and components to avoid double-calling within BadgeRow

* refactor: add try/catch

* chore: remove comment
This commit is contained in:
Danny Avila 2025-09-09 17:32:10 -04:00 committed by GitHub
parent 519645c0b0
commit 751c2e1d17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 435 additions and 115 deletions

View file

@ -368,7 +368,7 @@ function BadgeRow({
<CodeInterpreter />
<FileSearch />
<Artifacts />
<MCPSelect conversationId={conversationId} />
<MCPSelect />
</>
)}
{ghostBadge && (

View file

@ -3,22 +3,20 @@ import { MultiSelect, MCPIcon } from '@librechat/client';
import MCPServerStatusIcon from '~/components/MCP/MCPServerStatusIcon';
import MCPConfigDialog from '~/components/MCP/MCPConfigDialog';
import { useBadgeRowContext } from '~/Providers';
import { useMCPServerManager } from '~/hooks';
type MCPSelectProps = { conversationId?: string | null };
function MCPSelectContent({ conversationId }: MCPSelectProps) {
function MCPSelectContent() {
const { conversationId, mcpServerManager } = useBadgeRowContext();
const {
configuredServers,
mcpValues,
isPinned,
placeholderText,
batchToggleServers,
getServerStatusIconProps,
getConfigDialogProps,
isInitializing,
localize,
} = useMCPServerManager({ conversationId });
isPinned,
mcpValues,
isInitializing,
placeholderText,
configuredServers,
batchToggleServers,
getConfigDialogProps,
getServerStatusIconProps,
} = mcpServerManager;
const renderSelectedValues = useCallback(
(values: string[], placeholder?: string) => {
@ -103,10 +101,10 @@ function MCPSelectContent({ conversationId }: MCPSelectProps) {
);
}
function MCPSelect(props: MCPSelectProps) {
function MCPSelect() {
const { mcpServerNames } = useBadgeRowContext();
if ((mcpServerNames?.length ?? 0) === 0) return null;
return <MCPSelectContent {...props} />;
return <MCPSelectContent />;
}
export default memo(MCPSelect);

View file

@ -4,27 +4,27 @@ import { ChevronRight } from 'lucide-react';
import { PinIcon, MCPIcon } from '@librechat/client';
import MCPServerStatusIcon from '~/components/MCP/MCPServerStatusIcon';
import MCPConfigDialog from '~/components/MCP/MCPConfigDialog';
import { useMCPServerManager } from '~/hooks';
import { useBadgeRowContext } from '~/Providers';
import { cn } from '~/utils';
interface MCPSubMenuProps {
placeholder?: string;
conversationId?: string | null;
}
const MCPSubMenu = React.forwardRef<HTMLDivElement, MCPSubMenuProps>(
({ placeholder, conversationId, ...props }, ref) => {
({ placeholder, ...props }, ref) => {
const { mcpServerManager } = useBadgeRowContext();
const {
configuredServers,
mcpValues,
isPinned,
mcpValues,
setIsPinned,
isInitializing,
placeholderText,
configuredServers,
getConfigDialogProps,
toggleServerSelection,
getServerStatusIconProps,
getConfigDialogProps,
isInitializing,
} = useMCPServerManager({ conversationId });
} = mcpServerManager;
const menuStore = Ariakit.useMenuStore({
focusLoop: true,

View file

@ -31,7 +31,6 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => {
fileSearch,
agentsConfig,
mcpServerNames,
conversationId,
codeApiKeyForm,
codeInterpreter,
searchApiKeyForm,
@ -290,9 +289,7 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => {
if (mcpServerNames && mcpServerNames.length > 0) {
dropdownItems.push({
hideOnClick: false,
render: (props) => (
<MCPSubMenu {...props} placeholder={mcpPlaceholder} conversationId={conversationId} />
),
render: (props) => <MCPSubMenu {...props} placeholder={mcpPlaceholder} />,
});
}