mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Feat: List MCP servers #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
211
web/src/hooks/use-mcp-request.ts
Normal file
211
web/src/hooks/use-mcp-request.ts
Normal file
@ -0,0 +1,211 @@
|
||||
import message from '@/components/ui/message';
|
||||
import { IMcpServerListResponse } from '@/interfaces/database/mcp';
|
||||
import i18n from '@/locales/config';
|
||||
import mcpServerService from '@/services/mcp-server-service';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useState } from 'react';
|
||||
|
||||
export const enum McpApiAction {
|
||||
ListMcpServer = 'listMcpServer',
|
||||
GetMcpServer = 'getMcpServer',
|
||||
CreateMcpServer = 'createMcpServer',
|
||||
UpdateMcpServer = 'updateMcpServer',
|
||||
DeleteMcpServer = 'deleteMcpServer',
|
||||
ImportMcpServer = 'importMcpServer',
|
||||
ExportMcpServer = 'exportMcpServer',
|
||||
ListMcpServerTools = 'listMcpServerTools',
|
||||
TestMcpServerTool = 'testMcpServerTool',
|
||||
CacheMcpServerTool = 'cacheMcpServerTool',
|
||||
TestMcpServer = 'testMcpServer',
|
||||
}
|
||||
|
||||
export const useListMcpServer = () => {
|
||||
const { data, isFetching: loading } = useQuery<IMcpServerListResponse>({
|
||||
queryKey: [McpApiAction.ListMcpServer],
|
||||
initialData: { total: 0, mcp_servers: [] },
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
const { data } = await mcpServerService.list({});
|
||||
return data?.data;
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading };
|
||||
};
|
||||
|
||||
export const useGetMcpServer = () => {
|
||||
const [id, setId] = useState('');
|
||||
const { data, isFetching: loading } = useQuery({
|
||||
queryKey: [McpApiAction.GetMcpServer, id],
|
||||
initialData: {},
|
||||
gcTime: 0,
|
||||
enabled: !!id,
|
||||
queryFn: async () => {
|
||||
const { data } = await mcpServerService.get();
|
||||
return data?.data ?? {};
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, setId, id };
|
||||
};
|
||||
|
||||
export const useCreateMcpServer = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [McpApiAction.CreateMcpServer],
|
||||
mutationFn: async (params: Record<string, any>) => {
|
||||
const { data = {} } = await mcpServerService.create(params);
|
||||
if (data.code === 0) {
|
||||
message.success(i18n.t(`message.created`));
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [McpApiAction.ListMcpServer],
|
||||
});
|
||||
}
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, createMcpServer: mutateAsync };
|
||||
};
|
||||
|
||||
export const useUpdateMcpServer = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [McpApiAction.UpdateMcpServer],
|
||||
mutationFn: async (params: Record<string, any>) => {
|
||||
const { data = {} } = await mcpServerService.update(params);
|
||||
if (data.code === 0) {
|
||||
message.success(i18n.t(`message.updated`));
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [McpApiAction.ListMcpServer],
|
||||
});
|
||||
}
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, updateMcpServer: mutateAsync };
|
||||
};
|
||||
|
||||
export const useDeleteMcpServer = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [McpApiAction.DeleteMcpServer],
|
||||
mutationFn: async (params: Record<string, any>) => {
|
||||
const { data = {} } = await mcpServerService.delete(params);
|
||||
if (data.code === 0) {
|
||||
message.success(i18n.t(`message.deleted`));
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [McpApiAction.ListMcpServer],
|
||||
});
|
||||
}
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, deleteMcpServer: mutateAsync };
|
||||
};
|
||||
|
||||
export const useImportMcpServer = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [McpApiAction.ImportMcpServer],
|
||||
mutationFn: async (params: Record<string, any>) => {
|
||||
const { data = {} } = await mcpServerService.import(params);
|
||||
if (data.code === 0) {
|
||||
message.success(i18n.t(`message.created`));
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [McpApiAction.ListMcpServer],
|
||||
});
|
||||
}
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, importMcpServer: mutateAsync };
|
||||
};
|
||||
|
||||
export const useListMcpServerTools = () => {
|
||||
const { data, isFetching: loading } = useQuery({
|
||||
queryKey: [McpApiAction.ListMcpServerTools],
|
||||
initialData: [],
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
const { data } = await mcpServerService.listTools();
|
||||
return data?.data ?? [];
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading };
|
||||
};
|
||||
|
||||
export const useTestMcpServer = () => {
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [McpApiAction.TestMcpServer],
|
||||
mutationFn: async (params: Record<string, any>) => {
|
||||
const { data = {} } = await mcpServerService.test(params);
|
||||
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, testMcpServer: mutateAsync };
|
||||
};
|
||||
|
||||
export const useCacheMcpServerTool = () => {
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [McpApiAction.CacheMcpServerTool],
|
||||
mutationFn: async (params: Record<string, any>) => {
|
||||
const { data = {} } = await mcpServerService.cacheTool(params);
|
||||
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, cacheMcpServerTool: mutateAsync };
|
||||
};
|
||||
|
||||
export const useTestMcpServerTool = () => {
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [McpApiAction.TestMcpServerTool],
|
||||
mutationFn: async (params: Record<string, any>) => {
|
||||
const { data = {} } = await mcpServerService.testTool(params);
|
||||
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, testMcpServerTool: mutateAsync };
|
||||
};
|
||||
15
web/src/interfaces/database/mcp.ts
Normal file
15
web/src/interfaces/database/mcp.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export interface IMcpServer {
|
||||
create_date: string;
|
||||
description: null;
|
||||
id: string;
|
||||
name: string;
|
||||
server_type: string;
|
||||
update_date: string;
|
||||
url: string;
|
||||
variables: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface IMcpServerListResponse {
|
||||
mcp_servers: IMcpServer[];
|
||||
total: number;
|
||||
}
|
||||
31
web/src/pages/profile-setting/mcp/edit-mcp-dialog.tsx
Normal file
31
web/src/pages/profile-setting/mcp/edit-mcp-dialog.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import { ButtonLoading } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { EditMcpForm, FormId } from './edit-mcp-form';
|
||||
|
||||
export function EditMcpDialog({ hideModal, loading, onOk }: IModalProps<any>) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={hideModal}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Edit profile</DialogTitle>
|
||||
</DialogHeader>
|
||||
<EditMcpForm onOk={onOk} hideModal={hideModal}></EditMcpForm>
|
||||
<DialogFooter>
|
||||
<ButtonLoading type="submit" form={FormId} loading={loading}>
|
||||
{t('common.save')}
|
||||
</ButtonLoading>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
138
web/src/pages/profile-setting/mcp/edit-mcp-form.tsx
Normal file
138
web/src/pages/profile-setting/mcp/edit-mcp-form.tsx
Normal file
@ -0,0 +1,138 @@
|
||||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { RAGFlowSelect } from '@/components/ui/select';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { buildOptions } from '@/utils/form';
|
||||
import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const FormId = 'EditMcpForm';
|
||||
|
||||
enum ServerType {
|
||||
SSE = 'sse',
|
||||
StreamableHttp = 'streamable-http',
|
||||
}
|
||||
|
||||
const ServerTypeOptions = buildOptions(ServerType);
|
||||
|
||||
export function EditMcpForm({
|
||||
initialName,
|
||||
hideModal,
|
||||
onOk,
|
||||
}: IModalProps<any> & { initialName?: string }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const FormSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(1, {
|
||||
message: t('common.namePlaceholder'),
|
||||
})
|
||||
.trim(),
|
||||
url: z
|
||||
.string()
|
||||
.min(1, {
|
||||
message: t('common.namePlaceholder'),
|
||||
})
|
||||
.trim(),
|
||||
server_type: z
|
||||
.string()
|
||||
.min(1, {
|
||||
message: t('common.namePlaceholder'),
|
||||
})
|
||||
.trim(),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: { name: '', server_type: ServerType.SSE, url: '' },
|
||||
});
|
||||
|
||||
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||
const ret = await onOk?.(data);
|
||||
if (ret) {
|
||||
hideModal?.();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (initialName) {
|
||||
form.setValue('name', initialName);
|
||||
}
|
||||
}, [form, initialName]);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-6"
|
||||
id={FormId}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('common.name')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t('common.namePlaceholder')}
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="url"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('common.url')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t('common.namePlaceholder')}
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="server_type"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('common.serverType')}</FormLabel>
|
||||
<FormControl>
|
||||
<RAGFlowSelect
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
options={ServerTypeOptions}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
42
web/src/pages/profile-setting/mcp/index.tsx
Normal file
42
web/src/pages/profile-setting/mcp/index.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { SearchInput } from '@/components/ui/input';
|
||||
import { useListMcpServer } from '@/hooks/use-mcp-request';
|
||||
import { Import, Plus } from 'lucide-react';
|
||||
import { EditMcpDialog } from './edit-mcp-dialog';
|
||||
import { McpCard } from './mcp-card';
|
||||
import { useEditMcp } from './use-edit-mcp';
|
||||
|
||||
const list = new Array(10).fill('1');
|
||||
export default function McpServer() {
|
||||
const { data } = useListMcpServer();
|
||||
const { editVisible, showEditModal, hideEditModal, handleOk } = useEditMcp();
|
||||
|
||||
return (
|
||||
<section className="p-4">
|
||||
<div className="text-text-title text-2xl">MCP Servers</div>
|
||||
<section className="flex items-center justify-between">
|
||||
<div className="text-text-sub-title">自定义 MCP Server 的列表</div>
|
||||
<div className="flex gap-5">
|
||||
<SearchInput className="w-40"></SearchInput>
|
||||
<Button variant={'secondary'}>
|
||||
<Import /> Import
|
||||
</Button>
|
||||
<Button onClick={showEditModal('')}>
|
||||
<Plus /> Add MCP
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
<section className="flex gap-5 flex-wrap pt-5">
|
||||
{data.mcp_servers.map((item) => (
|
||||
<McpCard key={item.id} data={item}></McpCard>
|
||||
))}
|
||||
</section>
|
||||
{editVisible && (
|
||||
<EditMcpDialog
|
||||
hideModal={hideEditModal}
|
||||
onOk={handleOk}
|
||||
></EditMcpDialog>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
44
web/src/pages/profile-setting/mcp/mcp-card.tsx
Normal file
44
web/src/pages/profile-setting/mcp/mcp-card.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { MoreButton } from '@/components/more-button';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { IMcpServer } from '@/interfaces/database/mcp';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { McpDropdown } from './mcp-dropdown';
|
||||
|
||||
export type DatasetCardProps = {
|
||||
data: IMcpServer;
|
||||
};
|
||||
|
||||
export function McpCard({ data }: DatasetCardProps) {
|
||||
const { navigateToAgent } = useNavigatePage();
|
||||
|
||||
return (
|
||||
<Card key={data.id} className="w-64" onClick={navigateToAgent(data.id)}>
|
||||
<CardContent className="p-2.5 pt-2 group">
|
||||
<section className="flex justify-between mb-2">
|
||||
<div className="flex gap-2 items-center">
|
||||
<Avatar className="size-6 rounded-lg">
|
||||
<AvatarImage src={data?.avatar} />
|
||||
<AvatarFallback className="rounded-lg ">CN</AvatarFallback>
|
||||
</Avatar>
|
||||
</div>
|
||||
<McpDropdown>
|
||||
<MoreButton></MoreButton>
|
||||
</McpDropdown>
|
||||
</section>
|
||||
<div className="flex justify-between items-end">
|
||||
<div className="w-full">
|
||||
<h3 className="text-lg font-semibold mb-2 line-clamp-1">
|
||||
{data.name}
|
||||
</h3>
|
||||
<p className="text-xs text-text-sub-title">{data.description}</p>
|
||||
<p className="text-xs text-text-sub-title">
|
||||
{formatDate(data.update_date)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
48
web/src/pages/profile-setting/mcp/mcp-dropdown.tsx
Normal file
48
web/src/pages/profile-setting/mcp/mcp-dropdown.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { PenLine, Trash2 } from 'lucide-react';
|
||||
import { MouseEventHandler, PropsWithChildren, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function McpDropdown({ children }: PropsWithChildren) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleShowAgentRenameModal: MouseEventHandler<HTMLDivElement> =
|
||||
useCallback((e) => {
|
||||
e.stopPropagation();
|
||||
}, []);
|
||||
|
||||
const handleDelete: MouseEventHandler<HTMLDivElement> =
|
||||
useCallback(() => {}, []);
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={handleShowAgentRenameModal}>
|
||||
{t('common.rename')} <PenLine />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<ConfirmDeleteDialog onOk={handleDelete}>
|
||||
<DropdownMenuItem
|
||||
className="text-text-delete-red"
|
||||
onSelect={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{t('common.delete')} <Trash2 />
|
||||
</DropdownMenuItem>
|
||||
</ConfirmDeleteDialog>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
49
web/src/pages/profile-setting/mcp/use-edit-mcp.ts
Normal file
49
web/src/pages/profile-setting/mcp/use-edit-mcp.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import {
|
||||
useCreateMcpServer,
|
||||
useGetMcpServer,
|
||||
useUpdateMcpServer,
|
||||
} from '@/hooks/use-mcp-request';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useEditMcp = () => {
|
||||
const {
|
||||
visible: editVisible,
|
||||
hideModal: hideEditModal,
|
||||
showModal: showEditModal,
|
||||
} = useSetModalState();
|
||||
const { createMcpServer, loading } = useCreateMcpServer();
|
||||
const { data, setId, id } = useGetMcpServer();
|
||||
const { updateMcpServer } = useUpdateMcpServer();
|
||||
|
||||
const handleShowModal = useCallback(
|
||||
(id?: string) => () => {
|
||||
if (id) {
|
||||
setId(id);
|
||||
}
|
||||
showEditModal();
|
||||
},
|
||||
[setId, showEditModal],
|
||||
);
|
||||
|
||||
const handleOk = useCallback(
|
||||
async (values: any) => {
|
||||
if (id) {
|
||||
updateMcpServer(values);
|
||||
} else {
|
||||
createMcpServer(values);
|
||||
}
|
||||
},
|
||||
[createMcpServer, id, updateMcpServer],
|
||||
);
|
||||
|
||||
return {
|
||||
editVisible,
|
||||
hideEditModal,
|
||||
showEditModal: handleShowModal,
|
||||
loading,
|
||||
createMcpServer,
|
||||
detail: data,
|
||||
handleOk,
|
||||
};
|
||||
};
|
||||
@ -1,8 +1,5 @@
|
||||
import {
|
||||
ProfileSettingBaseKey,
|
||||
ProfileSettingRouteKey,
|
||||
} from '@/constants/setting';
|
||||
import { useLogout } from '@/hooks/login-hooks';
|
||||
import { Routes } from '@/routes';
|
||||
import { useCallback } from 'react';
|
||||
import { useNavigate } from 'umi';
|
||||
|
||||
@ -11,11 +8,11 @@ export const useHandleMenuClick = () => {
|
||||
const { logout } = useLogout();
|
||||
|
||||
const handleMenuClick = useCallback(
|
||||
(key: ProfileSettingRouteKey) => () => {
|
||||
if (key === ProfileSettingRouteKey.Logout) {
|
||||
(key: Routes) => () => {
|
||||
if (key === Routes.Logout) {
|
||||
logout();
|
||||
} else {
|
||||
navigate(`/${ProfileSettingBaseKey}/${key}`);
|
||||
navigate(`${Routes.ProfileSetting}${key}`);
|
||||
}
|
||||
},
|
||||
[logout, navigate],
|
||||
|
||||
@ -2,10 +2,10 @@ import { useIsDarkTheme, useTheme } from '@/components/theme-provider';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { ProfileSettingRouteKey } from '@/constants/setting';
|
||||
import { useLogout } from '@/hooks/login-hooks';
|
||||
import { useSecondPathName } from '@/hooks/route-hook';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Routes } from '@/routes';
|
||||
import {
|
||||
AlignEndVertical,
|
||||
Banknote,
|
||||
@ -22,9 +22,10 @@ const menuItems = [
|
||||
{
|
||||
section: 'Account & collaboration',
|
||||
items: [
|
||||
{ icon: User, label: 'Profile', key: ProfileSettingRouteKey.Profile },
|
||||
{ icon: LayoutGrid, label: 'Team', key: ProfileSettingRouteKey.Team },
|
||||
{ icon: Banknote, label: 'Plan', key: ProfileSettingRouteKey.Plan },
|
||||
{ icon: User, label: 'Profile', key: Routes.Profile },
|
||||
{ icon: LayoutGrid, label: 'Team', key: Routes.Team },
|
||||
{ icon: Banknote, label: 'Plan', key: Routes.Plan },
|
||||
{ icon: Banknote, label: 'MCP', key: Routes.Mcp },
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -33,17 +34,17 @@ const menuItems = [
|
||||
{
|
||||
icon: Box,
|
||||
label: 'Model management',
|
||||
key: ProfileSettingRouteKey.Model,
|
||||
key: Routes.Model,
|
||||
},
|
||||
{
|
||||
icon: FileCog,
|
||||
label: 'Prompt management',
|
||||
key: ProfileSettingRouteKey.Prompt,
|
||||
key: Routes.Prompt,
|
||||
},
|
||||
{
|
||||
icon: AlignEndVertical,
|
||||
label: 'Chunking method',
|
||||
key: ProfileSettingRouteKey.Chunk,
|
||||
key: Routes.Chunk,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export enum Routes {
|
||||
Login = '/login',
|
||||
Logout = '/logout',
|
||||
Home = '/home',
|
||||
Datasets = '/datasets',
|
||||
DatasetBase = '/dataset',
|
||||
@ -13,6 +14,18 @@ export enum Routes {
|
||||
Chat = '/next-chat',
|
||||
Files = '/files',
|
||||
ProfileSetting = '/profile-setting',
|
||||
Profile = '/profile',
|
||||
Mcp = '/mcp',
|
||||
Team = '/team',
|
||||
Plan = '/plan',
|
||||
Model = '/model',
|
||||
Prompt = '/prompt',
|
||||
ProfileMcp = `${ProfileSetting}${Mcp}`,
|
||||
ProfileTeam = `${ProfileSetting}${Team}`,
|
||||
ProfilePlan = `${ProfileSetting}${Plan}`,
|
||||
ProfileModel = `${ProfileSetting}${Model}`,
|
||||
ProfilePrompt = `${ProfileSetting}${Prompt}`,
|
||||
ProfileProfile = `${ProfileSetting}${Profile}`,
|
||||
DatasetTesting = '/testing',
|
||||
DatasetSetting = '/setting',
|
||||
Chunk = '/chunk',
|
||||
@ -303,27 +316,31 @@ const routes = [
|
||||
routes: [
|
||||
{
|
||||
path: Routes.ProfileSetting,
|
||||
redirect: `${Routes.ProfileSetting}/profile`,
|
||||
redirect: `${Routes.ProfileProfile}`,
|
||||
},
|
||||
{
|
||||
path: `${Routes.ProfileSetting}/profile`,
|
||||
component: `@/pages${Routes.ProfileSetting}/profile`,
|
||||
path: `${Routes.ProfileProfile}`,
|
||||
component: `@/pages${Routes.ProfileProfile}`,
|
||||
},
|
||||
{
|
||||
path: `${Routes.ProfileSetting}/team`,
|
||||
component: `@/pages${Routes.ProfileSetting}/team`,
|
||||
path: `${Routes.ProfileTeam}`,
|
||||
component: `@/pages${Routes.ProfileTeam}`,
|
||||
},
|
||||
{
|
||||
path: `${Routes.ProfileSetting}/plan`,
|
||||
component: `@/pages${Routes.ProfileSetting}/plan`,
|
||||
path: `${Routes.ProfilePlan}`,
|
||||
component: `@/pages${Routes.ProfilePlan}`,
|
||||
},
|
||||
{
|
||||
path: `${Routes.ProfileSetting}/model`,
|
||||
component: `@/pages${Routes.ProfileSetting}/model`,
|
||||
path: `${Routes.ProfileModel}`,
|
||||
component: `@/pages${Routes.ProfileModel}`,
|
||||
},
|
||||
{
|
||||
path: `${Routes.ProfileSetting}/prompt`,
|
||||
component: `@/pages${Routes.ProfileSetting}/prompt`,
|
||||
path: `${Routes.ProfilePrompt}`,
|
||||
component: `@/pages${Routes.ProfilePrompt}`,
|
||||
},
|
||||
{
|
||||
path: Routes.ProfileMcp,
|
||||
component: `@/pages${Routes.ProfileMcp}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@ -3,39 +3,66 @@ import registerServer from '@/utils/register-server';
|
||||
import request from '@/utils/request';
|
||||
|
||||
const {
|
||||
getMcpServerList,
|
||||
getMultipleMcpServers,
|
||||
listMcpServer,
|
||||
createMcpServer,
|
||||
updateMcpServer,
|
||||
deleteMcpServer,
|
||||
getMcpServer,
|
||||
importMcpServer,
|
||||
exportMcpServer,
|
||||
listMcpServerTools,
|
||||
testMcpServerTool,
|
||||
cacheMcpServerTool,
|
||||
testMcpServer,
|
||||
} = api;
|
||||
|
||||
const methods = {
|
||||
get_list: {
|
||||
url: getMcpServerList,
|
||||
method: 'get',
|
||||
},
|
||||
get_multiple: {
|
||||
url: getMultipleMcpServers,
|
||||
list: {
|
||||
url: listMcpServer,
|
||||
method: 'post',
|
||||
},
|
||||
add: {
|
||||
get: {
|
||||
url: getMcpServer,
|
||||
method: 'post',
|
||||
},
|
||||
create: {
|
||||
url: createMcpServer,
|
||||
method: 'post'
|
||||
method: 'post',
|
||||
},
|
||||
update: {
|
||||
url: updateMcpServer,
|
||||
method: 'post'
|
||||
method: 'post',
|
||||
},
|
||||
rm: {
|
||||
delete: {
|
||||
url: deleteMcpServer,
|
||||
method: 'post'
|
||||
method: 'post',
|
||||
},
|
||||
import: {
|
||||
url: importMcpServer,
|
||||
method: 'post',
|
||||
},
|
||||
export: {
|
||||
url: exportMcpServer,
|
||||
method: 'post',
|
||||
},
|
||||
listTools: {
|
||||
url: listMcpServerTools,
|
||||
method: 'get',
|
||||
},
|
||||
testTool: {
|
||||
url: testMcpServerTool,
|
||||
method: 'post',
|
||||
},
|
||||
cacheTool: {
|
||||
url: cacheMcpServerTool,
|
||||
method: 'post',
|
||||
},
|
||||
test: {
|
||||
url: testMcpServer,
|
||||
method: 'post',
|
||||
},
|
||||
} as const;
|
||||
|
||||
const mcpServerService = registerServer<keyof typeof methods>(methods, request);
|
||||
|
||||
export const getMcpServer = (serverId: string) =>
|
||||
request.get(api.getMcpServer(serverId));
|
||||
|
||||
export default mcpServerService;
|
||||
|
||||
@ -148,10 +148,15 @@ export default {
|
||||
trace: `${api_host}/canvas/trace`,
|
||||
|
||||
// mcp server
|
||||
getMcpServerList: `${api_host}/mcp_server/list`,
|
||||
getMultipleMcpServers: `${api_host}/mcp_server/get_multiple`,
|
||||
getMcpServer: (serverId: string) => `${api_host}/mcp_server/get/${serverId}`,
|
||||
listMcpServer: `${api_host}/mcp_server/list`,
|
||||
getMcpServer: `${api_host}/mcp_server/detail`,
|
||||
createMcpServer: `${api_host}/mcp_server/create`,
|
||||
updateMcpServer: `${api_host}/mcp_server/update`,
|
||||
deleteMcpServer: `${api_host}/mcp_server/rm`,
|
||||
importMcpServer: `${api_host}/mcp_server/import`,
|
||||
exportMcpServer: `${api_host}/mcp_server/export`,
|
||||
listMcpServerTools: `${api_host}/mcp_server/list_tools`,
|
||||
testMcpServerTool: `${api_host}/mcp_server/test_tool`,
|
||||
cacheMcpServerTool: `${api_host}/mcp_server/cache_tools`,
|
||||
testMcpServer: `${api_host}/mcp_server/test_mcp`,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user