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',
|
error_desc: 'API is configured but connection error',
|
||||||
|
|
||||||
testConnectionSuccess: 'Test Connection Successful',
|
testConnectionSuccess: 'Test Connection Successful',
|
||||||
|
refreshSuccess: 'Refresh Successful',
|
||||||
|
refreshFailed: 'Refresh Failed',
|
||||||
serviceEndpoint: 'Service Endpoint URL',
|
serviceEndpoint: 'Service Endpoint URL',
|
||||||
serviceEndpointPlaceholder: 'URL of the service endpoint',
|
serviceEndpointPlaceholder: 'URL of the service endpoint',
|
||||||
serviceEndpointExtra: 'Complete access address of the MCP service',
|
serviceEndpointExtra: 'Complete access address of the MCP service',
|
||||||
|
|||||||
@@ -1803,6 +1803,8 @@ export const zh = {
|
|||||||
error_desc: 'API 已配置但链接异常',
|
error_desc: 'API 已配置但链接异常',
|
||||||
|
|
||||||
testConnectionSuccess: '测试连接成功',
|
testConnectionSuccess: '测试连接成功',
|
||||||
|
refreshSuccess: '刷新成功',
|
||||||
|
refreshFailed: '刷新失败',
|
||||||
serviceEndpoint: '服务端点 URL',
|
serviceEndpoint: '服务端点 URL',
|
||||||
serviceEndpointPlaceholder: '服务端点的 URL',
|
serviceEndpointPlaceholder: '服务端点的 URL',
|
||||||
serviceEndpointExtra: 'MCP服务的完整访问地址',
|
serviceEndpointExtra: 'MCP服务的完整访问地址',
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import InfiniteScroll from 'react-infinite-scroll-component';
|
|||||||
import MarketConfigModal, { type MarketConfigModalRef } from './components/MarketConfigModal';
|
import MarketConfigModal, { type MarketConfigModalRef } from './components/MarketConfigModal';
|
||||||
import McpServiceModal from './components/McpServiceModal';
|
import McpServiceModal from './components/McpServiceModal';
|
||||||
import type { McpServiceModalRef } from './types';
|
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 {
|
interface MarketSource {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -32,6 +32,7 @@ interface MarketMcp {
|
|||||||
tags?: string[];
|
tags?: string[];
|
||||||
view_count?: number;
|
view_count?: number;
|
||||||
activated?: boolean;
|
activated?: boolean;
|
||||||
|
inDatabase?: boolean;
|
||||||
locales?: {
|
locales?: {
|
||||||
[lang: string]: {
|
[lang: string]: {
|
||||||
name: 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 });
|
const res: any = await getMarketMCPs({ mcp_market_config_id: configId, page, pagesize: pageSize });
|
||||||
if (res?.items && Array.isArray(res.items)) {
|
if (res?.items && Array.isArray(res.items)) {
|
||||||
// 标记已激活的 MCP
|
// 标记已激活和已入库的 MCP
|
||||||
const mcpsWithActivated = res.items.map((item: MarketMcp) => ({
|
const mcpsWithActivated = res.items.map((item: MarketMcp) => {
|
||||||
...item,
|
// 检查是否已入库:market_id = sourceId, market_config_id = configId, mcp_service_id = item.id
|
||||||
activated: activatedIds.includes(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 => ({
|
setMcpCache(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
@@ -212,9 +227,14 @@ const Market: React.FC<{ getStatusTag?: (status: string) => ReactNode }> = () =>
|
|||||||
mcp_market_config_id: configIdMap[selectedSource],
|
mcp_market_config_id: configIdMap[selectedSource],
|
||||||
server_id: mcp.id,
|
server_id: mcp.id,
|
||||||
});
|
});
|
||||||
|
const source = marketSources.find(s => s.id === selectedSource);
|
||||||
const toolItem = {
|
const toolItem = {
|
||||||
name: detail.name,
|
name: detail.name,
|
||||||
description: detail.description,
|
description: detail.description,
|
||||||
|
source_channel: source?.name || '',
|
||||||
|
market_id: selectedSource,
|
||||||
|
market_config_id: configIdMap[selectedSource],
|
||||||
|
mcp_service_id: mcp.id,
|
||||||
config_data: {
|
config_data: {
|
||||||
server_url: detail.servers?.[0]?.url || '',
|
server_url: detail.servers?.[0]?.url || '',
|
||||||
connection_config: {
|
connection_config: {
|
||||||
@@ -392,8 +412,11 @@ const Market: React.FC<{ getStatusTag?: (status: string) => ReactNode }> = () =>
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={`rb:flex rb:items-center ${mcp.activated ? 'rb:justify-between' : 'rb:justify-end'}`}>
|
<div className={`rb:flex rb:items-center ${mcp.activated || mcp.inDatabase ? 'rb:justify-between' : 'rb:justify-end'}`}>
|
||||||
{mcp.activated && <Tag color="success">已激活</Tag>}
|
<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 type="primary" size="small" onClick={() => handleOpenMcpServiceModal(mcp)}>
|
||||||
+ 添加
|
+ 添加
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ const Mcp: React.FC<{ getStatusTag: (status: string) => ReactNode }> = ({ getSta
|
|||||||
getData()
|
getData()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除服务
|
// 删除服务
|
||||||
const handleDeleteService = (item: ToolItem) => {
|
const handleDeleteService = (item: ToolItem) => {
|
||||||
if (!item.id) {
|
if (!item.id) {
|
||||||
|
|||||||
@@ -87,6 +87,10 @@ const McpServiceModal = forwardRef<McpServiceModalRef, McpServiceModalProps>(({
|
|||||||
name, description, icon,
|
name, description, icon,
|
||||||
...(config_data ? { config: { ...config_data } } : {})
|
...(config_data ? { config: { ...config_data } } : {})
|
||||||
})
|
})
|
||||||
|
// 如果是从 Market 组件传来的数据(包含 market_id),保存完整的 data 用于后续提交
|
||||||
|
if ((data as any).market_id) {
|
||||||
|
setEditVo(data)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
form.resetFields();
|
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)
|
const request = editVo?.id ? updateTool(editVo.id, newService) : addTool(newService)
|
||||||
request.then((res: any) => {
|
request.then((res: any) => {
|
||||||
message.success(t('common.saveSuccess'));
|
message.success(t('common.saveSuccess'));
|
||||||
|
|||||||
@@ -75,6 +75,10 @@ export interface ToolItem {
|
|||||||
tool_class: string;
|
tool_class: string;
|
||||||
|
|
||||||
schema_content: string;
|
schema_content: string;
|
||||||
|
source_channel?: string;
|
||||||
|
market_id?: string;
|
||||||
|
market_config_id?: string;
|
||||||
|
mcp_service_id?: string;
|
||||||
};
|
};
|
||||||
status: 'available' | 'unavailable';
|
status: 'available' | 'unavailable';
|
||||||
tags: string[];
|
tags: string[];
|
||||||
|
|||||||
Reference in New Issue
Block a user