💾 chore: Enhance Local Storage Handling and Update MCP SDK (#6809)

* feat: Update MCP package version and dependencies; refactor ToolContentPart type

* refactor: Change module type to commonjs and update rollup configuration, remove unused dev dependency

* refactor: Change async calls to synchronous for MCP and FlowStateManager retrieval

* chore: Add eslint disable comment for i18next rule in DropdownPopup component

* fix: improve statefulness of mcp servers selected if some were removed since last session

* feat: implement conversation storage cleanup functions and integrate them into mutation success handlers

* feat: enhance storage condition logic in useLocalStorageAlt to prevent unnecessary local storage writes

* refactor: streamline local storage update logic in useLocalStorageAlt
This commit is contained in:
Danny Avila 2025-04-09 18:38:48 -04:00 committed by GitHub
parent 24c0433dcf
commit e16a6190a5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 420 additions and 382 deletions

View file

@ -17,6 +17,20 @@ import useLocalStorage from '~/hooks/useLocalStorageAlt';
import { useVerifyAgentToolAuth } from '~/data-provider';
import { ephemeralAgentByConvoId } from '~/store';
const storageCondition = (value: unknown, rawCurrentValue?: string | null) => {
if (rawCurrentValue) {
try {
const currentValue = rawCurrentValue?.trim() ?? '';
if (currentValue === 'true' && value === false) {
return true;
}
} catch (e) {
console.error(e);
}
}
return value !== undefined && value !== null && value !== '' && value !== false;
};
function CodeInterpreter({ conversationId }: { conversationId?: string | null }) {
const localize = useLocalize();
const key = conversationId ?? Constants.NEW_CONVO;
@ -55,6 +69,7 @@ function CodeInterpreter({ conversationId }: { conversationId?: string | null })
`${LocalStorageKeys.LAST_CODE_TOGGLE_}${key}`,
isCodeToggleEnabled,
setValue,
storageCondition,
);
const handleChange = useCallback(

View file

@ -8,14 +8,43 @@ import { ephemeralAgentByConvoId } from '~/store';
import MCPIcon from '~/components/ui/MCPIcon';
import { useLocalize } from '~/hooks';
const storageCondition = (value: unknown, rawCurrentValue?: string | null) => {
if (rawCurrentValue) {
try {
const currentValue = rawCurrentValue?.trim() ?? '';
if (currentValue.length > 2) {
return true;
}
} catch (e) {
console.error(e);
}
}
return Array.isArray(value) && value.length > 0;
};
function MCPSelect({ conversationId }: { conversationId?: string | null }) {
const localize = useLocalize();
const hasSetFetched = useRef(false);
const key = conversationId ?? Constants.NEW_CONVO;
const hasSetFetched = useRef<string | null>(null);
const { data: mcpServerSet, isFetched } = useAvailableToolsQuery(EModelEndpoint.agents, {
select: (data) => {
const serverNames = new Set<string>();
data.forEach((tool) => {
if (tool.pluginKey.includes(Constants.mcp_delimiter)) {
const parts = tool.pluginKey.split(Constants.mcp_delimiter);
serverNames.add(parts[parts.length - 1]);
}
});
return serverNames;
},
});
const [ephemeralAgent, setEphemeralAgent] = useRecoilState(ephemeralAgentByConvoId(key));
const mcpState = useMemo(() => {
return ephemeralAgent?.mcp ?? [];
}, [ephemeralAgent?.mcp]);
const setSelectedValues = useCallback(
(values: string[] | null | undefined) => {
if (!values) {
@ -35,33 +64,23 @@ function MCPSelect({ conversationId }: { conversationId?: string | null }) {
`${LocalStorageKeys.LAST_MCP_}${key}`,
mcpState,
setSelectedValues,
storageCondition,
);
const { data: mcpServers, isFetched } = useAvailableToolsQuery(EModelEndpoint.agents, {
select: (data) => {
const serverNames = new Set<string>();
data.forEach((tool) => {
if (tool.pluginKey.includes(Constants.mcp_delimiter)) {
const parts = tool.pluginKey.split(Constants.mcp_delimiter);
serverNames.add(parts[parts.length - 1]);
}
});
return [...serverNames];
},
});
useEffect(() => {
if (hasSetFetched.current) {
if (hasSetFetched.current === key) {
return;
}
if (!isFetched) {
return;
}
hasSetFetched.current = true;
if ((mcpServers?.length ?? 0) > 0) {
hasSetFetched.current = key;
if ((mcpServerSet?.size ?? 0) > 0) {
setMCPValues(mcpValues.filter((mcp) => mcpServerSet?.has(mcp)));
return;
}
setMCPValues([]);
}, [isFetched, setMCPValues, mcpServers?.length]);
}, [isFetched, setMCPValues, mcpServerSet, key, mcpValues]);
const renderSelectedValues = useCallback(
(values: string[], placeholder?: string) => {
@ -76,7 +95,11 @@ function MCPSelect({ conversationId }: { conversationId?: string | null }) {
[localize],
);
if (!mcpServers || mcpServers.length === 0) {
const mcpServers = useMemo(() => {
return Array.from(mcpServerSet ?? []);
}, [mcpServerSet]);
if (!mcpServerSet || mcpServerSet.size === 0) {
return null;
}

View file

@ -3,6 +3,7 @@ import { useClearConversationsMutation } from 'librechat-data-provider/react-que
import { Label, Button, OGDialog, OGDialogTrigger, Spinner } from '~/components';
import { useLocalize, useNewConvo } from '~/hooks';
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
import { clearAllConversationStorage } from '~/utils';
export const ClearChats = () => {
const localize = useLocalize();
@ -15,6 +16,7 @@ export const ClearChats = () => {
{},
{
onSuccess: () => {
clearAllConversationStorage();
newConversation();
},
},

View file

@ -83,6 +83,7 @@ const DropdownPopup: React.FC<DropdownProps> = ({
)}
{item.label}
{item.kbd != null && (
// eslint-disable-next-line i18next/no-literal-string
<kbd className="ml-auto hidden font-sans text-xs text-black/50 group-hover:inline group-focus:inline dark:text-white/50">
{item.kbd}
</kbd>