Feat: Delete MCP server #3221 (#8772)

### What problem does this PR solve?

Feat: Delete MCP server #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-07-10 14:28:27 +08:00
committed by GitHub
parent 2e0905d06a
commit 9f94d88acd
7 changed files with 56 additions and 19 deletions

View File

@ -106,8 +106,8 @@ export const useDeleteMcpServer = () => {
mutateAsync, mutateAsync,
} = useMutation({ } = useMutation({
mutationKey: [McpApiAction.DeleteMcpServer], mutationKey: [McpApiAction.DeleteMcpServer],
mutationFn: async (params: Record<string, any>) => { mutationFn: async (ids: string[]) => {
const { data = {} } = await mcpServerService.delete(params); const { data = {} } = await mcpServerService.delete({ mcp_ids: ids });
if (data.code === 0) { if (data.code === 0) {
message.success(i18n.t(`message.deleted`)); message.success(i18n.t(`message.deleted`));

View File

@ -685,6 +685,7 @@ export const initialWaitingDialogueValues = {};
export const initialAgentValues = { export const initialAgentValues = {
...initialLlmBaseValues, ...initialLlmBaseValues,
description: '', description: '',
user_prompt: '',
sys_prompt: ``, sys_prompt: ``,
prompts: [{ role: PromptRole.User, content: `{${AgentGlobals.SysQuery}}` }], prompts: [{ role: PromptRole.User, content: `{${AgentGlobals.SysQuery}}` }],
message_history_window_size: 12, message_history_window_size: 12,

View File

@ -12,6 +12,7 @@ import {
} from '@/components/ui/form'; } from '@/components/ui/form';
import { Input, NumberInput } from '@/components/ui/input'; import { Input, NumberInput } from '@/components/ui/input';
import { RAGFlowSelect } from '@/components/ui/select'; import { RAGFlowSelect } from '@/components/ui/select';
import { Textarea } from '@/components/ui/textarea';
import { buildOptions } from '@/utils/form'; import { buildOptions } from '@/utils/form';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useMemo } from 'react'; import { useMemo } from 'react';
@ -39,6 +40,7 @@ const exceptionMethodOptions = buildOptions(AgentExceptionMethod);
const FormSchema = z.object({ const FormSchema = z.object({
sys_prompt: z.string(), sys_prompt: z.string(),
description: z.string().optional(), description: z.string().optional(),
user_prompt: z.string().optional(),
prompts: z.string().optional(), prompts: z.string().optional(),
// prompts: z // prompts: z
// .array( // .array(
@ -98,7 +100,23 @@ const AgentForm = ({ node }: INextOperatorForm) => {
}} }}
> >
<FormContainer> <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> <LargeModelFormField></LargeModelFormField>
<FormField <FormField
control={form.control} control={form.control}

View File

@ -56,6 +56,12 @@ import {
getNodeDragHandle, getNodeDragHandle,
} from '../utils'; } from '../utils';
function isBottomSubAgent(type: string, position: Position) {
return (
(type === Operator.Agent && position === Position.Bottom) ||
type === Operator.Tool
);
}
export const useInitializeOperatorParams = () => { export const useInitializeOperatorParams = () => {
const llmId = useFetchModelId(); const llmId = useFetchModelId();
@ -114,8 +120,17 @@ export const useInitializeOperatorParams = () => {
}, [llmId]); }, [llmId]);
const initializeOperatorParams = useCallback( const initializeOperatorParams = useCallback(
(operatorName: Operator) => { (operatorName: Operator, position: Position) => {
return initialFormValuesMap[operatorName]; 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], [initialFormValuesMap],
); );
@ -235,13 +250,6 @@ function useAddToolNode() {
return { addToolNode }; return { addToolNode };
} }
function isBottomSubAgent(type: string, position: Position) {
return (
(type === Operator.Agent && position === Position.Bottom) ||
type === Operator.Tool
);
}
function useResizeIterationNode() { function useResizeIterationNode() {
const { getNode, nodes, updateNode } = useGraphStore((state) => state); const { getNode, nodes, updateNode } = useGraphStore((state) => state);
@ -324,7 +332,7 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
getNodeName(type), getNodeName(type),
nodes, nodes,
), ),
form: initializeOperatorParams(type as Operator), form: initializeOperatorParams(type as Operator, params.position),
}, },
sourcePosition: Position.Right, sourcePosition: Position.Right,
targetPosition: Position.Left, targetPosition: Position.Left,

View File

@ -35,7 +35,7 @@ export function McpCard({
<section className="flex justify-between pb-2"> <section className="flex justify-between pb-2">
<h3 className="text-lg font-semibold line-clamp-1">{data.name}</h3> <h3 className="text-lg font-semibold line-clamp-1">{data.name}</h3>
<div className="space-x-4"> <div className="space-x-4">
<McpDropdown> <McpDropdown mcpId={data.id}>
<MoreButton></MoreButton> <MoreButton></MoreButton>
</McpDropdown> </McpDropdown>
<Checkbox <Checkbox

View File

@ -6,27 +6,33 @@ import {
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { useDeleteMcpServer } from '@/hooks/use-mcp-request';
import { PenLine, Trash2 } from 'lucide-react'; import { PenLine, Trash2 } from 'lucide-react';
import { MouseEventHandler, PropsWithChildren, useCallback } from 'react'; import { MouseEventHandler, PropsWithChildren, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export function McpDropdown({ children }: PropsWithChildren) { export function McpDropdown({
children,
mcpId,
}: PropsWithChildren & { mcpId: string }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { deleteMcpServer } = useDeleteMcpServer();
const handleShowAgentRenameModal: MouseEventHandler<HTMLDivElement> = const handleShowAgentRenameModal: MouseEventHandler<HTMLDivElement> =
useCallback((e) => { useCallback((e) => {
e.stopPropagation(); e.stopPropagation();
}, []); }, []);
const handleDelete: MouseEventHandler<HTMLDivElement> = const handleDelete: MouseEventHandler<HTMLDivElement> = useCallback(() => {
useCallback(() => {}, []); deleteMcpServer([mcpId]);
}, [deleteMcpServer, mcpId]);
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger> <DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
<DropdownMenuItem onClick={handleShowAgentRenameModal}> <DropdownMenuItem onClick={handleShowAgentRenameModal}>
{t('common.rename')} <PenLine /> {t('common.edit')} <PenLine />
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<ConfirmDeleteDialog onOk={handleDelete}> <ConfirmDeleteDialog onOk={handleDelete}>

View File

@ -1,3 +1,4 @@
import { useDeleteMcpServer } from '@/hooks/use-mcp-request';
import { Trash2, Upload } from 'lucide-react'; import { Trash2, Upload } from 'lucide-react';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -5,10 +6,13 @@ import { useTranslation } from 'react-i18next';
export function useBulkOperateMCP() { export function useBulkOperateMCP() {
const { t } = useTranslation(); const { t } = useTranslation();
const [selectedList, setSelectedList] = useState<Array<string>>([]); const [selectedList, setSelectedList] = useState<Array<string>>([]);
const { deleteMcpServer } = useDeleteMcpServer();
const handleEnableClick = useCallback(() => {}, []); const handleEnableClick = useCallback(() => {}, []);
const handleDelete = useCallback(() => {}, []); const handleDelete = useCallback(() => {
deleteMcpServer(selectedList);
}, [deleteMcpServer, selectedList]);
const handleSelectChange = useCallback((id: string, checked: boolean) => { const handleSelectChange = useCallback((id: string, checked: boolean) => {
setSelectedList((list) => { setSelectedList((list) => {