fix: delete mcp market

This commit is contained in:
WangCham
2025-10-28 13:11:09 +08:00
parent 9f2f1cd577
commit 166eebabff
4 changed files with 64 additions and 59 deletions
@@ -168,7 +168,7 @@ export default function MCPMarketComponent({
</div> </div>
) : installedServers.length === 0 ? ( ) : installedServers.length === 0 ? (
<div style={{ textAlign: 'center', padding: '20px' }}> <div style={{ textAlign: 'center', padding: '20px' }}>
{t('mcp.noInstalledServers')} {t('mcp.noServerInstalled')}
</div> </div>
) : ( ) : (
installedServers.map((server, index) => ( installedServers.map((server, index) => (
@@ -1,4 +1,4 @@
import { MCPMarketCardVO } from '@/app/home/plugins/mcp-market/mcp-market-card/MCPMarketCardVO'; import { MCPMarketCardVO } from '@/app/home/plugins/mcp-server/mcp-market-card/MCPServerCardVO';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
+62 -57
View File
@@ -7,7 +7,7 @@ import MarketPage from '@/app/home/plugins/plugin-market/PluginMarketComponent';
import MCPComponent, { import MCPComponent, {
MCPComponentRef, MCPComponentRef,
} from '@/app/home/plugins/mcp/MCPComponent'; } from '@/app/home/plugins/mcp/MCPComponent';
import MCPMarketComponent from '@/app/home/plugins/mcp-market/MCPMarketComponent'; import MCPServerComponent from '@/app/home/plugins/mcp-server/MCPServerComponent';
import styles from './plugins.module.css'; import styles from './plugins.module.css';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@@ -48,7 +48,7 @@ import {
SelectItem, SelectItem,
} from "@/components/ui/select" } from "@/components/ui/select"
import { useForm } from 'react-hook-form'; import { Resolver, useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod'; import { z } from 'zod';
import { DialogDescription } from '@radix-ui/react-dialog'; import { DialogDescription } from '@radix-ui/react-dialog';
@@ -121,26 +121,48 @@ export default function PluginConfigPage() {
form.setValue('extra_args', newArgs); form.setValue('extra_args', newArgs);
}; };
const getFormSchema = (t: (key: string) => string) => const getFormSchema = (t: (key: string) => string) =>
z.object({ z.object({
name: z.string().min(1, { message: t('mcp.nameRequired') }), name: z.string({ required_error: t('mcp.nameRequired') }),
timeout: z.number().min(30, { message: t('mcp.timeoutMin30') }), timeout: z
ssereadtimeout: z .number({ invalid_type_error: t('mcp.timeoutMustBeNumber') })
.number() .nonnegative({ message: t('mcp.timeoutNonNegative') })
.min(300, { message: t('mcp.sseTimeoutMin300') }), .default(30),
url: z.string().min(1, { message: t('mcp.requestURLRequired') }), ssereadtimeout: z
extra_args: z.array(getExtraArgSchema(t)).optional(), .number({ invalid_type_error: t('mcp.sseTimeoutMustBeNumber') })
}); .nonnegative({ message: t('mcp.sseTimeoutNonNegative') })
const formSchema = getFormSchema(t); .default(300),
const form = useForm<z.infer<typeof formSchema>>({ url: z.string({ required_error: t('models.requestURLRequired') }),
resolver: zodResolver(formSchema), extra_args: z
defaultValues: { .array(
name: '', z.object({
url: '', key: z.string(),
timeout: 30, type: z.enum(['string', 'number', 'boolean']),
ssereadtimeout: 300, value: z.string(),
extra_args: [], })
}, )
.optional(),
}); });
const formSchema = getFormSchema(t);
type FormValues = z.infer<typeof formSchema> & {
timeout: number;
ssereadtimeout: number;
};
const form = useForm<FormValues>({
resolver: zodResolver(formSchema) as unknown as Resolver<FormValues>,
defaultValues: {
name: '',
url: '',
timeout: 30,
ssereadtimeout: 300,
extra_args: [],
},
});
const [extraArgs, setExtraArgs] = useState< const [extraArgs, setExtraArgs] = useState<
{ key: string; type: 'string' | 'number' | 'boolean'; value: string }[] { key: string; type: 'string' | 'number' | 'boolean'; value: string }[]
>([]); >([]);
@@ -274,24 +296,12 @@ export default function PluginConfigPage() {
}); });
if (!mcpSSEModalOpen && !modalOpen && !showDeleteConfirmModal) { if (!mcpSSEModalOpen && !modalOpen && !showDeleteConfirmModal) {
console.log(
'[Dialog Debug] All dialogs closed, cleaning up body styles...',
);
console.log(
'[Dialog Debug] Before cleanup - body.style.pointerEvents:',
document.body.style.pointerEvents,
);
console.log(
'[Dialog Debug] Before cleanup - body.style.overflow:',
document.body.style.overflow,
);
const cleanup = () => { const cleanup = () => {
// 强制移除 body 上可能残留的样式
document.body.style.removeProperty('pointer-events'); document.body.style.removeProperty('pointer-events');
document.body.style.removeProperty('overflow'); document.body.style.removeProperty('overflow');
// 如果 removeProperty 不起作用,强制设置为空字符串
if (document.body.style.pointerEvents === 'none') { if (document.body.style.pointerEvents === 'none') {
document.body.style.pointerEvents = ''; document.body.style.pointerEvents = '';
} }
@@ -334,7 +344,7 @@ export default function PluginConfigPage() {
} }
}, [mcpSSEModalOpen, modalOpen, showDeleteConfirmModal]); }, [mcpSSEModalOpen, modalOpen, showDeleteConfirmModal]);
// 额外的全局清理:定期检查并清理
useEffect(() => { useEffect(() => {
const interval = setInterval(() => { const interval = setInterval(() => {
if (!mcpSSEModalOpen && !modalOpen && !showDeleteConfirmModal) { if (!mcpSSEModalOpen && !modalOpen && !showDeleteConfirmModal) {
@@ -454,16 +464,16 @@ export default function PluginConfigPage() {
async function loadServerForEdit(serverName: string) { async function loadServerForEdit(serverName: string) {
try { try {
const resp = await httpClient.getMCPServer(serverName); const resp = await httpClient.getMCPServer(serverName);
const server = resp.server ?? resp; // 有的接口包了一层,有的直接返回对象 const server = resp.server ?? resp;
console.log('Loaded server for edit:', server); console.log('Loaded server for edit:', server);
// 填充表单数据
form.setValue('name', server.name); form.setValue('name', server.name);
form.setValue('url', server.extra_args?.url || ''); form.setValue('url', server.extra_args?.url || '');
form.setValue('timeout', server.extra_args?.timeout || 30); form.setValue('timeout', server.extra_args?.timeout || 30);
form.setValue('ssereadtimeout', server.extra_args?.ssereadtimeout || 300); form.setValue('ssereadtimeout', server.extra_args?.ssereadtimeout || 300);
// 填充 headers
if (server.extra_args?.headers) { if (server.extra_args?.headers) {
const headers = Object.entries(server.extra_args.headers).map( const headers = Object.entries(server.extra_args.headers).map(
([key, value]) => ({ ([key, value]) => ({
@@ -476,23 +486,23 @@ export default function PluginConfigPage() {
form.setValue('extra_args', headers); form.setValue('extra_args', headers);
} }
// 重置测试状态
setMcpTestStatus('testing'); setMcpTestStatus('testing');
setMcpToolNames([]); setMcpToolNames([]);
setMcpTestError(''); setMcpTestError('');
// 打开对话框
setEditingServerName(serverName); setEditingServerName(serverName);
setIsEditMode(true); setIsEditMode(true);
setMcpSSEModalOpen(true); setMcpSSEModalOpen(true);
// 在这里测试工具连接状态
try { try {
const res = await httpClient.testMCPServer(server.name); const res = await httpClient.testMCPServer(server.name);
if (res.task_id) { if (res.task_id) {
const taskId = res.task_id; const taskId = res.task_id;
// 监听任务完成
const interval = setInterval(() => { const interval = setInterval(() => {
httpClient.getAsyncTask(taskId).then((taskResp) => { httpClient.getAsyncTask(taskId).then((taskResp) => {
console.log('Task response:', taskResp); console.log('Task response:', taskResp);
@@ -505,13 +515,13 @@ export default function PluginConfigPage() {
console.log('Exception:', taskResp.runtime.exception); console.log('Exception:', taskResp.runtime.exception);
if (taskResp.runtime.exception) { if (taskResp.runtime.exception) {
// 测试失败
console.log('Test failed with exception'); console.log('Test failed with exception');
setMcpTestStatus('failed'); setMcpTestStatus('failed');
setMcpToolNames([]); setMcpToolNames([]);
setMcpTestError(taskResp.runtime.exception || '未知错误'); setMcpTestError(taskResp.runtime.exception || '未知错误');
} else if (taskResp.runtime.result) { } else if (taskResp.runtime.result) {
// 测试成功 - 后端可能返回字符串或对象
try { try {
let result: { let result: {
status?: string; status?: string;
@@ -520,7 +530,7 @@ export default function PluginConfigPage() {
error?: string; error?: string;
}; };
// 如果result是字符串,需要先解析
const rawResult: any = taskResp.runtime.result; const rawResult: any = taskResp.runtime.result;
if (typeof rawResult === 'string') { if (typeof rawResult === 'string') {
console.log('Result is string, parsing...'); console.log('Result is string, parsing...');
@@ -614,25 +624,21 @@ export default function PluginConfigPage() {
}; };
if (isEditMode && editingServerName) { if (isEditMode && editingServerName) {
// 编辑模式:更新服务器
await httpClient.updateMCPServer(editingServerName, serverConfig); await httpClient.updateMCPServer(editingServerName, serverConfig);
toast.success(t('mcp.updateSuccess')); toast.success(t('mcp.updateSuccess'));
} else { } else {
// 创建模式:新建服务器
await httpClient.createMCPServer(serverConfig); await httpClient.createMCPServer(serverConfig);
toast.success(t('mcp.createSuccess')); toast.success(t('mcp.createSuccess'));
} }
// 只有在异步操作成功后才关闭对话框
setMcpSSEModalOpen(false); setMcpSSEModalOpen(false);
// 重置表单和状态
form.reset(); form.reset();
setExtraArgs([]); setExtraArgs([]);
setEditingServerName(null); setEditingServerName(null);
setIsEditMode(false); setIsEditMode(false);
// 刷新服务器列表
setRefreshKey((prev) => prev + 1); setRefreshKey((prev) => prev + 1);
} catch (error) { } catch (error) {
console.error('Failed to save MCP server:', error); console.error('Failed to save MCP server:', error);
@@ -706,7 +712,7 @@ export default function PluginConfigPage() {
if (file) { if (file) {
uploadPluginFile(file); uploadPluginFile(file);
} }
// 清空input值,以便可以重复选择同一个文件
event.target.value = ''; event.target.value = '';
}, },
[uploadPluginFile], [uploadPluginFile],
@@ -748,7 +754,7 @@ export default function PluginConfigPage() {
[uploadPluginFile, isPluginSystemReady, t], [uploadPluginFile, isPluginSystemReady, t],
); );
// 插件系统未启用的状态显示
const renderPluginDisabledState = () => ( const renderPluginDisabledState = () => (
<div className="flex flex-col items-center justify-center h-[60vh] text-center pt-[10vh]"> <div className="flex flex-col items-center justify-center h-[60vh] text-center pt-[10vh]">
<Power className="w-16 h-16 text-gray-400 mb-4" /> <Power className="w-16 h-16 text-gray-400 mb-4" />
@@ -761,7 +767,7 @@ export default function PluginConfigPage() {
</div> </div>
); );
// 插件系统连接异常的状态显示
const renderPluginConnectionErrorState = () => ( const renderPluginConnectionErrorState = () => (
<div className="flex flex-col items-center justify-center h-[60vh] text-center pt-[10vh]"> <div className="flex flex-col items-center justify-center h-[60vh] text-center pt-[10vh]">
<svg <svg
@@ -783,7 +789,6 @@ export default function PluginConfigPage() {
</div> </div>
); );
// 加载状态显示
const renderLoadingState = () => ( const renderLoadingState = () => (
<div className="flex flex-col items-center justify-center h-[60vh] pt-[10vh]"> <div className="flex flex-col items-center justify-center h-[60vh] pt-[10vh]">
<p className="text-gray-500 dark:text-gray-400"> <p className="text-gray-500 dark:text-gray-400">
@@ -792,7 +797,6 @@ export default function PluginConfigPage() {
</div> </div>
); );
// 根据状态返回不同的内容
if (statusLoading) { if (statusLoading) {
return renderLoadingState(); return renderLoadingState();
} }
@@ -930,7 +934,7 @@ export default function PluginConfigPage() {
<MCPComponent ref={mcpComponentRef} /> <MCPComponent ref={mcpComponentRef} />
</TabsContent> </TabsContent>
<TabsContent value="mcp-servers"> <TabsContent value="mcp-servers">
<MCPMarketComponent <MCPServerComponent
key={refreshKey} key={refreshKey}
onEditServer={(serverName) => { onEditServer={(serverName) => {
loadServerForEdit(serverName); loadServerForEdit(serverName);
@@ -1221,6 +1225,7 @@ export default function PluginConfigPage() {
)} )}
/> />
<FormItem> <FormItem>
<FormLabel>{t('models.extraParameters')}</FormLabel> <FormLabel>{t('models.extraParameters')}</FormLabel>
<div className="space-y-2"> <div className="space-y-2">