From 68372a4b7af7fa07eec9d594b79ba2c653db2c52 Mon Sep 17 00:00:00 2001 From: wangcham Date: Mon, 13 Oct 2025 12:51:58 +0000 Subject: [PATCH] feat: add mcp from sse on frontend --- web/src/app/home/plugins/page.tsx | 114 ++++++++++++++++++------ web/src/app/infra/http/BackendClient.ts | 6 ++ web/src/i18n/locales/zh-Hans.ts | 13 +++ 3 files changed, 104 insertions(+), 29 deletions(-) diff --git a/web/src/app/home/plugins/page.tsx b/web/src/app/home/plugins/page.tsx index 94a96478..9122c77b 100644 --- a/web/src/app/home/plugins/page.tsx +++ b/web/src/app/home/plugins/page.tsx @@ -41,6 +41,9 @@ import { useTranslation } from 'react-i18next'; import { PluginV4 } from '@/app/infra/entities/plugin'; import { systemInfo } from '@/app/infra/http/HttpClient'; import { ApiRespPluginSystemStatus } from '@/app/infra/entities/api'; +import { set } from 'lodash'; +import { passiveEventSupported } from '@tanstack/react-table'; +import { config } from 'process'; enum PluginInstallStatus { WAIT_INPUT = 'wait_input', @@ -69,7 +72,7 @@ export default function PluginConfigPage() { ); const [mcpSSEHeaders,setMcpSSEHeaders] = useState('') const [mcpName,setMcpName] = useState('') - const [mcpTimeout,setMcpTimeout] = useState('') + const [mcpTimeout,setMcpTimeout] = useState(60) const [installError, setInstallError] = useState(null); const [mcpInstallError, setMcpInstallError] = useState(null); const [githubURL, setGithubURL] = useState(''); @@ -125,6 +128,7 @@ export default function PluginConfigPage() { } const [mcpGithubURL, setMcpGithubURL] = useState(''); const [mcpSSEURL, setMcpSSEURL] = useState(''); + const [mcpSSEConfig, setMcpSSEConfig] = useState | null>(null); const [mcpInstallConfig, setMcpInstallConfig] = useState | null>(null); const pluginInstalledRef = useRef(null); const mcpComponentRef = useRef(null); @@ -134,7 +138,7 @@ export default function PluginConfigPage() { } function handleMcpModalConfirm() { - installMcpServer(mcpGithubURL); + installMcpServerFromSSE(mcpSSEConfig ?? {}); } function installPlugin( installSource: string, @@ -316,18 +320,14 @@ export default function PluginConfigPage() { return renderPluginConnectionErrorState(); } - function installMcpServer(url: string, config?: Record) { + function installMcpServerFromSSE(config?: Record) { setMcpInstallStatus(PluginInstallStatus.INSTALLING); - // NOTE: backend currently only accepts url. If backend accepts config in future, - // replace this call with: httpClient.installMCPServerFromGithub(url, config) - console.log('installing mcp server with config:', config); - httpClient.installMCPServerFromGithub(url) + console.log('installing mcp server from sse with config:', config); + httpClient.installMCPServerFromSSE(config ?? {}) .then((resp) => { const taskId = resp.task_id; - let alreadySuccess = false; console.log('taskId:', taskId); - // 每秒拉取一次任务状态 const interval = setInterval(() => { httpClient.getAsyncTask(taskId).then((resp) => { @@ -343,8 +343,12 @@ export default function PluginConfigPage() { toast.success(t('mcp.installSuccess')); alreadySuccess = true; } - setMcpGithubURL(''); - setMcpMarketInstallModalOpen(false); + setMcpSSEInstallModalOpen(false); + setMcpName(''); + setMcpDescription(''); + setMcpSSEURL(''); + setMcpSSEHeaders(''); + setMcpTimeout(60); mcpComponentRef.current?.refreshServerList(); } } @@ -358,6 +362,48 @@ export default function PluginConfigPage() { }); } + // function installMcpServer(url: string, config?: Record) { + // setMcpInstallStatus(PluginInstallStatus.INSTALLING); + // // NOTE: backend currently only accepts url. If backend accepts config in future, + // // replace this call with: httpClient.installMCPServerFromGithub(url, config) + // console.log('installing mcp server with config:', config); + // httpClient.installMCPServerFromGithub(url) + // .then((resp) => { + // const taskId = resp.task_id; + + // let alreadySuccess = false; + // console.log('taskId:', taskId); + + // // 每秒拉取一次任务状态 + // const interval = setInterval(() => { + // httpClient.getAsyncTask(taskId).then((resp) => { + // console.log('task status:', resp); + // if (resp.runtime.done) { + // clearInterval(interval); + // if (resp.runtime.exception) { + // setMcpInstallError(resp.runtime.exception); + // setMcpInstallStatus(PluginInstallStatus.ERROR); + // } else { + // // success + // if (!alreadySuccess) { + // toast.success(t('mcp.installSuccess')); + // alreadySuccess = true; + // } + // setMcpGithubURL(''); + // setMcpMarketInstallModalOpen(false); + // mcpComponentRef.current?.refreshServerList(); + // } + // } + // }); + // }, 1000); + // }) + // .catch((err) => { + // console.log('error when install mcp server:', err); + // setMcpInstallError(err.message); + // setMcpInstallStatus(PluginInstallStatus.ERROR); + // }); + // } + return (
{ setActiveTab('mcp-market'); - // setMcpMarketInstallModalOpen(true); + setMcpSSEInstallModalOpen(true); + setMcpInstallStatus(PluginInstallStatus.WAIT_INPUT); + setMcpInstallError(null); }} > @@ -586,8 +634,8 @@ export default function PluginConfigPage() { - setMcpName(e.target.value)} className='mb-1' @@ -598,10 +646,10 @@ export default function PluginConfigPage() {
setMcpDescription(e.target.value)} className='mb-1' @@ -613,7 +661,7 @@ export default function PluginConfigPage() {
setMcpSSEHeaders(e.target.value)} className="mb-1" @@ -645,8 +693,8 @@ export default function PluginConfigPage() { setMcpTimeout(e.target.value)} + value={mcpTimeout || 60} + onChange={(e) => setMcpTimeout(Number(e.target.value))} className="mb-1" />
@@ -676,6 +724,10 @@ export default function PluginConfigPage() { setMcpInstallError(null); setMcpInstallConfig(null); setMcpSSEURL('') + setMcpName('') + setMcpTimeout(60) + setMcpDescription('') + setMcpSSEHeaders('') }} > {t('common.cancel')} @@ -687,19 +739,23 @@ export default function PluginConfigPage() { toast.error(t('mcp.urlRequired')); return; } - if (!mcpName) ( - toast.error(t('')) - ) - - - + if (!mcpName) { + toast.error(t('mcp.nameRequired')); + } + if (!mcpTimeout) { + toast.error(t('mcp.timeoutRequired')); + } const configToSend = { - + name: mcpSSEConfig?.name, + description: mcpSSEConfig?.description, + sse_url: mcpSSEConfig?.sse_url, + sse_headers: mcpSSEConfig?.sse_headers, + timeout: Number(mcpSSEConfig?.timeout) || 60, }; handleMcpModalConfirm(); // call installer (for now installMcpServer will log config and call backend with url only) - installMcpServer(mcpGithubURL, configToSend); + installMcpServerFromSSE(configToSend); }} > {t('common.confirm')} @@ -865,7 +921,7 @@ export default function PluginConfigPage() { handleMcpModalConfirm(); // call installer (for now installMcpServer will log config and call backend with url only) - installMcpServer(mcpGithubURL, configToSend); + // installMcpServer(mcpGithubURL, configToSend); }} > {t('common.confirm')} diff --git a/web/src/app/infra/http/BackendClient.ts b/web/src/app/infra/http/BackendClient.ts index f27cea9d..fed5c00e 100644 --- a/web/src/app/infra/http/BackendClient.ts +++ b/web/src/app/infra/http/BackendClient.ts @@ -552,6 +552,12 @@ export class BackendClient extends BaseHttpClient { return this.post('/api/v1/mcp/install/github', { source }); } + public installMCPServerFromSSE( + source: {}, + ): Promise { + return this.post('/api/v1/mcp/install/sse', { source }); + } + // ============ System API ============ public getSystemInfo(): Promise { return this.get('/api/v1/system/info'); diff --git a/web/src/i18n/locales/zh-Hans.ts b/web/src/i18n/locales/zh-Hans.ts index c481607e..d81b7386 100644 --- a/web/src/i18n/locales/zh-Hans.ts +++ b/web/src/i18n/locales/zh-Hans.ts @@ -1,3 +1,4 @@ + const zhHans = { common: { login: '登录', @@ -335,6 +336,18 @@ const zhHans = { enterGithubLink: '输入Github仓库链接', add: '添加', name:'名称', + nameExplained:'用于区分不同的MCP服务器实例', + mcpDescription:'描述', + descriptionExplained:'简要描述这个MCP服务器的功能或用途', + sseURL:'SSE URL', + sseHeaders:'SSE Headers', + nameRequired:'名称不能为空', + sseURLRequired:'SSE URL不能为空', + enterSSELink:'输入SSE URL', + timeoutRequired:'超时时间不能为空', + headersExample:'示例: Authorization: Bearer token123', + enterTimeout:'输入超时时间,单位为毫秒', + installFromSSE:'从SSE安装', }, pipelines: { title: '流水线',