From 4c2b31f31f8aaf7cd98e99c29acc8179dd7c351f Mon Sep 17 00:00:00 2001 From: yujiangping Date: Mon, 9 Mar 2026 15:36:49 +0800 Subject: [PATCH] feat(web): add MCP market database tracking and refresh status messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add i18n translations for refresh success and failure messages in English and Chinese - Track MCP tools already stored in database with inDatabase flag in Market component - Display "已入库" (In Database) tag alongside activation status for MCPs - Import getTools API to fetch full tool list for database status comparison - Add market metadata fields (source_channel, market_id, market_config_id, mcp_service_id) to tool items when adding from market - Preserve market source information through McpServiceModal when saving tools - Update ToolItem type to include market tracking fields in config_data - Improve MCP card layout to properly display multiple status tags --- web/src/i18n/en.ts | 2 + web/src/i18n/zh.ts | 2 + web/src/views/ToolManagement/Market.tsx | 39 +++++++++++++++---- web/src/views/ToolManagement/Mcp.tsx | 1 - .../components/McpServiceModal.tsx | 13 +++++++ web/src/views/ToolManagement/types.ts | 4 ++ 6 files changed, 52 insertions(+), 9 deletions(-) diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index 19498811..3904b88b 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -1806,6 +1806,8 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re error_desc: 'API is configured but connection error', testConnectionSuccess: 'Test Connection Successful', + refreshSuccess: 'Refresh Successful', + refreshFailed: 'Refresh Failed', serviceEndpoint: 'Service Endpoint URL', serviceEndpointPlaceholder: 'URL of the service endpoint', serviceEndpointExtra: 'Complete access address of the MCP service', diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 664795fe..24e56d4e 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -1803,6 +1803,8 @@ export const zh = { error_desc: 'API 已配置但链接异常', testConnectionSuccess: '测试连接成功', + refreshSuccess: '刷新成功', + refreshFailed: '刷新失败', serviceEndpoint: '服务端点 URL', serviceEndpointPlaceholder: '服务端点的 URL', serviceEndpointExtra: 'MCP服务的完整访问地址', diff --git a/web/src/views/ToolManagement/Market.tsx b/web/src/views/ToolManagement/Market.tsx index 7a2df6df..00dea715 100644 --- a/web/src/views/ToolManagement/Market.tsx +++ b/web/src/views/ToolManagement/Market.tsx @@ -6,7 +6,7 @@ import InfiniteScroll from 'react-infinite-scroll-component'; import MarketConfigModal, { type MarketConfigModalRef } from './components/MarketConfigModal'; import McpServiceModal from './components/McpServiceModal'; import type { McpServiceModalRef } from './types'; -import { getMarketTools, getMarketConfig, getMarketMCPs, getMarketMCPDetail, getMarketMCPsActivated } from '@/api/tools'; +import { getMarketTools, getMarketConfig, getMarketMCPs, getMarketMCPDetail, getMarketMCPsActivated, getTools } from '@/api/tools'; interface MarketSource { id: string; name: string; @@ -32,6 +32,7 @@ interface MarketMcp { tags?: string[]; view_count?: number; activated?: boolean; + inDatabase?: boolean; locales?: { [lang: string]: { name: string; @@ -131,13 +132,27 @@ const Market: React.FC<{ getStatusTag?: (status: string) => ReactNode }> = () => } } + // 获取全量工具列表,用于标记已入库的 MCP + const allTools: any = await getTools({ tool_type: 'mcp' }); + const toolsList = Array.isArray(allTools) ? allTools : []; + const res: any = await getMarketMCPs({ mcp_market_config_id: configId, page, pagesize: pageSize }); if (res?.items && Array.isArray(res.items)) { - // 标记已激活的 MCP - const mcpsWithActivated = res.items.map((item: MarketMcp) => ({ - ...item, - activated: activatedIds.includes(item.id) - })); + // 标记已激活和已入库的 MCP + const mcpsWithActivated = res.items.map((item: MarketMcp) => { + // 检查是否已入库:market_id = sourceId, market_config_id = configId, mcp_service_id = item.id + const isInDatabase = toolsList.some((tool: any) => + tool.config_data?.market_id === sourceId && + tool.config_data?.market_config_id === configId && + tool.config_data?.mcp_service_id === item.id + ); + + return { + ...item, + activated: activatedIds.includes(item.id), + inDatabase: isInDatabase + }; + }); setMcpCache(prev => ({ ...prev, @@ -212,9 +227,14 @@ const Market: React.FC<{ getStatusTag?: (status: string) => ReactNode }> = () => mcp_market_config_id: configIdMap[selectedSource], server_id: mcp.id, }); + const source = marketSources.find(s => s.id === selectedSource); const toolItem = { name: detail.name, description: detail.description, + source_channel: source?.name || '', + market_id: selectedSource, + market_config_id: configIdMap[selectedSource], + mcp_service_id: mcp.id, config_data: { server_url: detail.servers?.[0]?.url || '', connection_config: { @@ -392,8 +412,11 @@ const Market: React.FC<{ getStatusTag?: (status: string) => ReactNode }> = () => )} -
- {mcp.activated && 已激活} +
+
+ {mcp.activated && 已激活} + {mcp.inDatabase && 已入库} +
diff --git a/web/src/views/ToolManagement/Mcp.tsx b/web/src/views/ToolManagement/Mcp.tsx index 55cb73f5..90883b36 100644 --- a/web/src/views/ToolManagement/Mcp.tsx +++ b/web/src/views/ToolManagement/Mcp.tsx @@ -61,7 +61,6 @@ const Mcp: React.FC<{ getStatusTag: (status: string) => ReactNode }> = ({ getSta getData() }) }; - // 删除服务 const handleDeleteService = (item: ToolItem) => { if (!item.id) { diff --git a/web/src/views/ToolManagement/components/McpServiceModal.tsx b/web/src/views/ToolManagement/components/McpServiceModal.tsx index 0c9f6759..8bd44ccf 100644 --- a/web/src/views/ToolManagement/components/McpServiceModal.tsx +++ b/web/src/views/ToolManagement/components/McpServiceModal.tsx @@ -87,6 +87,10 @@ const McpServiceModal = forwardRef(({ name, description, icon, ...(config_data ? { config: { ...config_data } } : {}) }) + // 如果是从 Market 组件传来的数据(包含 market_id),保存完整的 data 用于后续提交 + if ((data as any).market_id) { + setEditVo(data) + } } else { form.resetFields(); } @@ -116,6 +120,15 @@ const McpServiceModal = forwardRef(({ } } } + + // 如果是从 Market 组件传来的数据,添加市场相关字段 + if ((editVo as any)?.market_id) { + (newService.config as any).source_channel = (editVo as any).source_channel; + (newService.config as any).market_id = (editVo as any).market_id; + (newService.config as any).market_config_id = (editVo as any).market_config_id; + (newService.config as any).mcp_service_id = (editVo as any).mcp_service_id; + } + const request = editVo?.id ? updateTool(editVo.id, newService) : addTool(newService) request.then((res: any) => { message.success(t('common.saveSuccess')); diff --git a/web/src/views/ToolManagement/types.ts b/web/src/views/ToolManagement/types.ts index 98976e28..5508b200 100644 --- a/web/src/views/ToolManagement/types.ts +++ b/web/src/views/ToolManagement/types.ts @@ -75,6 +75,10 @@ export interface ToolItem { tool_class: string; schema_content: string; + source_channel?: string; + market_id?: string; + market_config_id?: string; + mcp_service_id?: string; }; status: 'available' | 'unavailable'; tags: string[];