feat(web): add MCP market database tracking and refresh status messages
- 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
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -1803,6 +1803,8 @@ export const zh = {
|
||||
error_desc: 'API 已配置但链接异常',
|
||||
|
||||
testConnectionSuccess: '测试连接成功',
|
||||
refreshSuccess: '刷新成功',
|
||||
refreshFailed: '刷新失败',
|
||||
serviceEndpoint: '服务端点 URL',
|
||||
serviceEndpointPlaceholder: '服务端点的 URL',
|
||||
serviceEndpointExtra: 'MCP服务的完整访问地址',
|
||||
|
||||
@@ -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 }> = () =>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className={`rb:flex rb:items-center ${mcp.activated ? 'rb:justify-between' : 'rb:justify-end'}`}>
|
||||
{mcp.activated && <Tag color="success">已激活</Tag>}
|
||||
<div className={`rb:flex rb:items-center ${mcp.activated || mcp.inDatabase ? 'rb:justify-between' : 'rb:justify-end'}`}>
|
||||
<div className="rb:flex rb:gap-2">
|
||||
{mcp.activated && <Tag color="success">已激活</Tag>}
|
||||
{mcp.inDatabase && <Tag color="blue">已入库</Tag>}
|
||||
</div>
|
||||
<Button type="primary" size="small" onClick={() => handleOpenMcpServiceModal(mcp)}>
|
||||
+ 添加
|
||||
</Button>
|
||||
|
||||
@@ -61,7 +61,6 @@ const Mcp: React.FC<{ getStatusTag: (status: string) => ReactNode }> = ({ getSta
|
||||
getData()
|
||||
})
|
||||
};
|
||||
|
||||
// 删除服务
|
||||
const handleDeleteService = (item: ToolItem) => {
|
||||
if (!item.id) {
|
||||
|
||||
@@ -87,6 +87,10 @@ const McpServiceModal = forwardRef<McpServiceModalRef, McpServiceModalProps>(({
|
||||
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<McpServiceModalRef, McpServiceModalProps>(({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是从 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'));
|
||||
|
||||
@@ -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[];
|
||||
|
||||
Reference in New Issue
Block a user