### What problem does this PR solve? Feat: Delete MCP server #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.20.0
| @@ -106,8 +106,8 @@ export const useDeleteMcpServer = () => { | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: [McpApiAction.DeleteMcpServer], | |||
| mutationFn: async (params: Record<string, any>) => { | |||
| const { data = {} } = await mcpServerService.delete(params); | |||
| mutationFn: async (ids: string[]) => { | |||
| const { data = {} } = await mcpServerService.delete({ mcp_ids: ids }); | |||
| if (data.code === 0) { | |||
| message.success(i18n.t(`message.deleted`)); | |||
| @@ -685,6 +685,7 @@ export const initialWaitingDialogueValues = {}; | |||
| export const initialAgentValues = { | |||
| ...initialLlmBaseValues, | |||
| description: '', | |||
| user_prompt: '', | |||
| sys_prompt: ``, | |||
| prompts: [{ role: PromptRole.User, content: `{${AgentGlobals.SysQuery}}` }], | |||
| message_history_window_size: 12, | |||
| @@ -12,6 +12,7 @@ import { | |||
| } from '@/components/ui/form'; | |||
| import { Input, NumberInput } from '@/components/ui/input'; | |||
| import { RAGFlowSelect } from '@/components/ui/select'; | |||
| import { Textarea } from '@/components/ui/textarea'; | |||
| import { buildOptions } from '@/utils/form'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { useMemo } from 'react'; | |||
| @@ -39,6 +40,7 @@ const exceptionMethodOptions = buildOptions(AgentExceptionMethod); | |||
| const FormSchema = z.object({ | |||
| sys_prompt: z.string(), | |||
| description: z.string().optional(), | |||
| user_prompt: z.string().optional(), | |||
| prompts: z.string().optional(), | |||
| // prompts: z | |||
| // .array( | |||
| @@ -98,7 +100,23 @@ const AgentForm = ({ node }: INextOperatorForm) => { | |||
| }} | |||
| > | |||
| <FormContainer> | |||
| {isSubAgent && <DescriptionField></DescriptionField>} | |||
| {isSubAgent && ( | |||
| <> | |||
| <DescriptionField></DescriptionField> | |||
| <FormField | |||
| control={form.control} | |||
| name={`user_prompt`} | |||
| render={({ field }) => ( | |||
| <FormItem className="flex-1"> | |||
| <FormLabel>Subagent Input</FormLabel> | |||
| <FormControl> | |||
| <Textarea {...field}></Textarea> | |||
| </FormControl> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| </> | |||
| )} | |||
| <LargeModelFormField></LargeModelFormField> | |||
| <FormField | |||
| control={form.control} | |||
| @@ -56,6 +56,12 @@ import { | |||
| getNodeDragHandle, | |||
| } from '../utils'; | |||
| function isBottomSubAgent(type: string, position: Position) { | |||
| return ( | |||
| (type === Operator.Agent && position === Position.Bottom) || | |||
| type === Operator.Tool | |||
| ); | |||
| } | |||
| export const useInitializeOperatorParams = () => { | |||
| const llmId = useFetchModelId(); | |||
| @@ -114,8 +120,17 @@ export const useInitializeOperatorParams = () => { | |||
| }, [llmId]); | |||
| const initializeOperatorParams = useCallback( | |||
| (operatorName: Operator) => { | |||
| return initialFormValuesMap[operatorName]; | |||
| (operatorName: Operator, position: Position) => { | |||
| const initialValues = initialFormValuesMap[operatorName]; | |||
| if (isBottomSubAgent(operatorName, position)) { | |||
| return { | |||
| ...initialValues, | |||
| description: 'This is an agent for a specific task.', | |||
| user_prompt: 'This is the order you need to send to the agent.', | |||
| }; | |||
| } | |||
| return initialValues; | |||
| }, | |||
| [initialFormValuesMap], | |||
| ); | |||
| @@ -235,13 +250,6 @@ function useAddToolNode() { | |||
| return { addToolNode }; | |||
| } | |||
| function isBottomSubAgent(type: string, position: Position) { | |||
| return ( | |||
| (type === Operator.Agent && position === Position.Bottom) || | |||
| type === Operator.Tool | |||
| ); | |||
| } | |||
| function useResizeIterationNode() { | |||
| const { getNode, nodes, updateNode } = useGraphStore((state) => state); | |||
| @@ -324,7 +332,7 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) { | |||
| getNodeName(type), | |||
| nodes, | |||
| ), | |||
| form: initializeOperatorParams(type as Operator), | |||
| form: initializeOperatorParams(type as Operator, params.position), | |||
| }, | |||
| sourcePosition: Position.Right, | |||
| targetPosition: Position.Left, | |||
| @@ -35,7 +35,7 @@ export function McpCard({ | |||
| <section className="flex justify-between pb-2"> | |||
| <h3 className="text-lg font-semibold line-clamp-1">{data.name}</h3> | |||
| <div className="space-x-4"> | |||
| <McpDropdown> | |||
| <McpDropdown mcpId={data.id}> | |||
| <MoreButton></MoreButton> | |||
| </McpDropdown> | |||
| <Checkbox | |||
| @@ -6,27 +6,33 @@ import { | |||
| DropdownMenuSeparator, | |||
| DropdownMenuTrigger, | |||
| } from '@/components/ui/dropdown-menu'; | |||
| import { useDeleteMcpServer } from '@/hooks/use-mcp-request'; | |||
| import { PenLine, Trash2 } from 'lucide-react'; | |||
| import { MouseEventHandler, PropsWithChildren, useCallback } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| export function McpDropdown({ children }: PropsWithChildren) { | |||
| export function McpDropdown({ | |||
| children, | |||
| mcpId, | |||
| }: PropsWithChildren & { mcpId: string }) { | |||
| const { t } = useTranslation(); | |||
| const { deleteMcpServer } = useDeleteMcpServer(); | |||
| const handleShowAgentRenameModal: MouseEventHandler<HTMLDivElement> = | |||
| useCallback((e) => { | |||
| e.stopPropagation(); | |||
| }, []); | |||
| const handleDelete: MouseEventHandler<HTMLDivElement> = | |||
| useCallback(() => {}, []); | |||
| const handleDelete: MouseEventHandler<HTMLDivElement> = useCallback(() => { | |||
| deleteMcpServer([mcpId]); | |||
| }, [deleteMcpServer, mcpId]); | |||
| return ( | |||
| <DropdownMenu> | |||
| <DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger> | |||
| <DropdownMenuContent> | |||
| <DropdownMenuItem onClick={handleShowAgentRenameModal}> | |||
| {t('common.rename')} <PenLine /> | |||
| {t('common.edit')} <PenLine /> | |||
| </DropdownMenuItem> | |||
| <DropdownMenuSeparator /> | |||
| <ConfirmDeleteDialog onOk={handleDelete}> | |||
| @@ -1,3 +1,4 @@ | |||
| import { useDeleteMcpServer } from '@/hooks/use-mcp-request'; | |||
| import { Trash2, Upload } from 'lucide-react'; | |||
| import { useCallback, useState } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| @@ -5,10 +6,13 @@ import { useTranslation } from 'react-i18next'; | |||
| export function useBulkOperateMCP() { | |||
| const { t } = useTranslation(); | |||
| const [selectedList, setSelectedList] = useState<Array<string>>([]); | |||
| const { deleteMcpServer } = useDeleteMcpServer(); | |||
| const handleEnableClick = useCallback(() => {}, []); | |||
| const handleDelete = useCallback(() => {}, []); | |||
| const handleDelete = useCallback(() => { | |||
| deleteMcpServer(selectedList); | |||
| }, [deleteMcpServer, selectedList]); | |||
| const handleSelectChange = useCallback((id: string, checked: boolean) => { | |||
| setSelectedList((list) => { | |||