import { useState, useEffect } from 'react'; import { useFormContext, Controller } from 'react-hook-form'; import { Label, Checkbox, Spinner, useToastContext } from '@librechat/client'; import type { MCP } from 'librechat-data-provider'; import MCPAuth from '~/components/SidePanel/Builder/MCPAuth'; import MCPIcon from '~/components/SidePanel/Agents/MCPIcon'; import { MCPForm } from '~/common/types'; import { useLocalize } from '~/hooks'; function useUpdateAgentMCP({ onSuccess, onError, }: { onSuccess: (data: [string, MCP]) => void; onError: (error: Error) => void; }) { return { mutate: async ({ mcp_id, metadata, agent_id, }: { mcp_id?: string; metadata: MCP['metadata']; agent_id: string; }) => { try { // TODO: Implement MCP endpoint onSuccess(['success', { mcp_id, metadata, agent_id } as MCP]); } catch (error) { onError(error as Error); } }, isLoading: false, }; } interface MCPInputProps { mcp?: MCP; agent_id?: string; setMCP: React.Dispatch>; } export default function MCPInput({ mcp, agent_id, setMCP }: MCPInputProps) { const localize = useLocalize(); const { showToast } = useToastContext(); const { handleSubmit, register, formState: { errors }, control, } = useFormContext(); const [isLoading, setIsLoading] = useState(false); const [showTools, setShowTools] = useState(false); const [selectedTools, setSelectedTools] = useState([]); // Initialize tools list if editing existing MCP useEffect(() => { if (mcp?.mcp_id && mcp.metadata.tools) { setShowTools(true); setSelectedTools(mcp.metadata.tools); } }, [mcp]); const updateAgentMCP = useUpdateAgentMCP({ onSuccess(data) { showToast({ message: localize('com_ui_update_mcp_success'), status: 'success', }); setMCP(data[1]); setShowTools(true); setSelectedTools(data[1].metadata.tools ?? []); setIsLoading(false); }, onError(error) { showToast({ message: (error as Error).message || localize('com_ui_update_mcp_error'), status: 'error', }); setIsLoading(false); }, }); const saveMCP = handleSubmit(async (data: MCPForm) => { setIsLoading(true); try { const response = await updateAgentMCP.mutate({ agent_id: agent_id ?? '', mcp_id: mcp?.mcp_id, metadata: { ...data, tools: selectedTools, }, }); setMCP(response[1]); showToast({ message: localize('com_ui_update_mcp_success'), status: 'success', }); } catch { showToast({ message: localize('com_ui_update_mcp_error'), status: 'error', }); } finally { setIsLoading(false); } }); const handleSelectAll = () => { if (mcp?.metadata.tools) { setSelectedTools(mcp.metadata.tools); } }; const handleDeselectAll = () => { setSelectedTools([]); }; const handleToolToggle = (tool: string) => { setSelectedTools((prev) => prev.includes(tool) ? prev.filter((t) => t !== tool) : [...prev, tool], ); }; const handleToggleAll = () => { if (selectedTools.length === mcp?.metadata.tools?.length) { handleDeselectAll(); } else { handleSelectAll(); } }; const handleIconChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { const reader = new FileReader(); reader.onloadend = () => { const base64String = reader.result as string; setMCP({ mcp_id: mcp?.mcp_id ?? '', agent_id: agent_id ?? '', metadata: { ...mcp?.metadata, icon: base64String, }, }); }; reader.readAsDataURL(file); } }; return (
{/* Icon Picker */}
{/* name, description, url */}
{errors.name && ( {localize('com_ui_field_required')} )}
{errors.url && ( {errors.url.type === 'required' ? localize('com_ui_field_required') : errors.url.message} )}
( )} />
{errors.trust && ( {localize('com_ui_field_required')} )}
{showTools && mcp?.metadata.tools && (

{localize('com_ui_available_tools')}

{mcp.metadata.tools.map((tool) => ( ))}
)}
); }