* update: Add type * fix: update import statement for NextApiRequest type * fix: update imports to use type for LexicalEditor and EditorState * Refactor imports to use 'import type' for type-only imports across multiple files - Updated imports in various components and API files to use 'import type' for better clarity and to optimize TypeScript's type checking. - Ensured consistent usage of type imports in files related to chat, dataset, workflow, and user management. - Improved code readability and maintainability by distinguishing between value and type imports. * refactor: remove old ESLint configuration and add new rules - Deleted the old ESLint configuration file from the app project. - Added a new ESLint configuration file with updated rules and settings. - Changed imports to use type-only imports in various files for better clarity and performance. - Updated TypeScript configuration to remove unnecessary options. - Added an ESLint ignore file to exclude build and dependency directories from linting. * fix: update imports to use 'import type' for type-only imports in schema files
254 lines
7.2 KiB
TypeScript
254 lines
7.2 KiB
TypeScript
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
|
import { getMCPTools, postCreateMCPTools } from '@/web/core/app/api/plugin';
|
|
import {
|
|
Box,
|
|
Button,
|
|
Center,
|
|
Flex,
|
|
Input,
|
|
ModalBody,
|
|
ModalFooter,
|
|
Table,
|
|
TableContainer,
|
|
Tbody,
|
|
Td,
|
|
Th,
|
|
Thead,
|
|
Tr
|
|
} from '@chakra-ui/react';
|
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
|
import { useForm } from 'react-hook-form';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { AppListContext } from './context';
|
|
import { useContextSelector } from 'use-context-selector';
|
|
import { type ToolType } from '@fastgpt/global/core/app/type';
|
|
import type { getMCPToolsBody } from '@/pages/api/support/mcp/client/getTools';
|
|
|
|
export type MCPToolSetData = {
|
|
url: string;
|
|
toolList: ToolType[];
|
|
};
|
|
|
|
export type EditMCPToolsProps = {
|
|
avatar: string;
|
|
name: string;
|
|
mcpData: MCPToolSetData;
|
|
};
|
|
|
|
const MCPToolsEditModal = ({ onClose }: { onClose: () => void }) => {
|
|
const { t } = useTranslation();
|
|
const { toast } = useToast();
|
|
|
|
const { parentId, loadMyApps } = useContextSelector(AppListContext, (v) => v);
|
|
|
|
const { register, setValue, handleSubmit, watch } = useForm<EditMCPToolsProps>({
|
|
defaultValues: {
|
|
avatar: 'core/app/type/mcpToolsFill',
|
|
name: '',
|
|
mcpData: {
|
|
url: '',
|
|
toolList: []
|
|
}
|
|
}
|
|
});
|
|
const avatar = watch('avatar');
|
|
const mcpData = watch('mcpData');
|
|
|
|
const { runAsync: onCreate, loading: isCreating } = useRequest2(
|
|
async (data: EditMCPToolsProps) => {
|
|
return postCreateMCPTools({
|
|
name: data.name,
|
|
avatar: data.avatar,
|
|
toolList: data.mcpData.toolList,
|
|
url: data.mcpData.url,
|
|
parentId
|
|
});
|
|
},
|
|
{
|
|
onSuccess() {
|
|
onClose();
|
|
loadMyApps();
|
|
},
|
|
successToast: t('common:create_success'),
|
|
errorToast: t('common:create_failed')
|
|
}
|
|
);
|
|
|
|
const { runAsync: runGetMCPTools, loading: isGettingTools } = useRequest2(
|
|
(data: getMCPToolsBody) => getMCPTools(data),
|
|
{
|
|
onSuccess: (res: ToolType[]) => {
|
|
setValue('mcpData.toolList', res);
|
|
},
|
|
errorToast: t('app:MCP_tools_parse_failed')
|
|
}
|
|
);
|
|
|
|
const {
|
|
File,
|
|
onOpen: onOpenSelectFile,
|
|
onSelectImage
|
|
} = useSelectFile({
|
|
fileType: 'image/*',
|
|
multiple: false
|
|
});
|
|
|
|
return (
|
|
<>
|
|
<MyModal
|
|
isOpen={true}
|
|
onClose={onClose}
|
|
iconSrc="core/app/type/mcpToolsFill"
|
|
title={t('app:type.MCP tools')}
|
|
w={['90vw', '530px']}
|
|
position={'relative'}
|
|
>
|
|
<ModalBody>
|
|
<Box color={'myGray.900'} fontSize={'14px'} fontWeight={'medium'}>
|
|
{t('common:input_name')}
|
|
</Box>
|
|
<Flex mt={2} alignItems={'center'}>
|
|
<MyTooltip label={t('common:set_avatar')}>
|
|
<Avatar
|
|
flexShrink={0}
|
|
src={avatar}
|
|
w={['28px', '32px']}
|
|
h={['28px', '32px']}
|
|
cursor={'pointer'}
|
|
borderRadius={'md'}
|
|
onClick={onOpenSelectFile}
|
|
/>
|
|
</MyTooltip>
|
|
<Input
|
|
flex={1}
|
|
ml={4}
|
|
bg={'myWhite.600'}
|
|
{...register('name', {
|
|
required: t('common:name_is_empty')
|
|
})}
|
|
/>
|
|
</Flex>
|
|
|
|
<Box color={'myGray.900'} fontSize={'14px'} fontWeight={'medium'} mt={6}>
|
|
{t('app:MCP_tools_url')}
|
|
</Box>
|
|
<Flex alignItems={'center'} gap={2} mt={2}>
|
|
<Input
|
|
h={8}
|
|
placeholder={t('app:MCP_tools_url_placeholder')}
|
|
{...register('mcpData.url', {
|
|
required: t('app:MCP_tools_url_is_empty')
|
|
})}
|
|
/>
|
|
<Button
|
|
size={'sm'}
|
|
variant={'whitePrimary'}
|
|
h={8}
|
|
isLoading={isGettingTools}
|
|
onClick={() => {
|
|
runGetMCPTools({ url: mcpData.url });
|
|
}}
|
|
>
|
|
{t('common:Parse')}
|
|
</Button>
|
|
</Flex>
|
|
|
|
<Box color={'myGray.900'} fontSize={'14px'} fontWeight={'medium'} mt={6}>
|
|
{t('app:MCP_tools_list')}
|
|
</Box>
|
|
<Box
|
|
mt={2}
|
|
borderRadius={'md'}
|
|
overflow={'hidden'}
|
|
borderWidth={'1px'}
|
|
position={'relative'}
|
|
>
|
|
<TableContainer maxH={360} minH={200} overflowY={'auto'}>
|
|
<Table bg={'white'}>
|
|
<Thead bg={'myGray.50'}>
|
|
<Th fontSize={'mini'} py={0} h={'34px'}>
|
|
{t('common:Name')}
|
|
</Th>
|
|
<Th fontSize={'mini'} py={0} h={'34px'}>
|
|
{t('common:plugin.Description')}
|
|
</Th>
|
|
</Thead>
|
|
<Tbody>
|
|
{mcpData.toolList.map((item) => (
|
|
<Tr key={item.name} height={'28px'}>
|
|
<Td
|
|
fontSize={'mini'}
|
|
color={'myGray.900'}
|
|
fontWeight={'medium'}
|
|
py={2}
|
|
maxW={1 / 2}
|
|
overflow={'hidden'}
|
|
textOverflow={'ellipsis'}
|
|
whiteSpace={'nowrap'}
|
|
>
|
|
{item.name}
|
|
</Td>
|
|
<Td
|
|
fontSize={'mini'}
|
|
color={'myGray.900'}
|
|
fontWeight={'medium'}
|
|
py={2}
|
|
maxW={1 / 2}
|
|
overflow={'hidden'}
|
|
textOverflow={'ellipsis'}
|
|
whiteSpace={'nowrap'}
|
|
>
|
|
{item.description}
|
|
</Td>
|
|
</Tr>
|
|
))}
|
|
</Tbody>
|
|
</Table>
|
|
</TableContainer>
|
|
{mcpData.toolList.length === 0 && (
|
|
<Center
|
|
position={'absolute'}
|
|
top={0}
|
|
left={0}
|
|
right={0}
|
|
bottom={0}
|
|
fontSize={'mini'}
|
|
color={'myGray.500'}
|
|
>
|
|
{t('app:no_mcp_tools_list')}
|
|
</Center>
|
|
)}
|
|
</Box>
|
|
</ModalBody>
|
|
<ModalFooter gap={2}>
|
|
<Button variant={'whitePrimary'} onClick={onClose}>
|
|
{t('common:Close')}
|
|
</Button>
|
|
<Button
|
|
isDisabled={mcpData.toolList.length === 0}
|
|
isLoading={isCreating}
|
|
onClick={handleSubmit(onCreate)}
|
|
>
|
|
{t('common:comfirn_create')}
|
|
</Button>
|
|
</ModalFooter>
|
|
</MyModal>
|
|
<File
|
|
onSelect={(e) =>
|
|
onSelectImage(e, {
|
|
maxH: 300,
|
|
maxW: 300,
|
|
callback: (e) => setValue('avatar', e)
|
|
})
|
|
}
|
|
/>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default MCPToolsEditModal;
|