diff --git a/web/static/js/chat.js b/web/static/js/chat.js index aaef6694..15377ff0 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -6,6 +6,7 @@ let mentionToolsLoaded = false; let mentionToolsLoadingPromise = null; let mentionSuggestionsEl = null; let mentionFilteredTools = []; +let externalMcpNames = []; // 外部MCP名称列表 const mentionState = { active: false, startIndex: -1, @@ -242,6 +243,23 @@ async function fetchMentionTools() { const collected = []; try { + // 同时获取外部MCP列表 + try { + const mcpResponse = await apiFetch('/api/external-mcp'); + if (mcpResponse.ok) { + const mcpData = await mcpResponse.json(); + externalMcpNames = Object.keys(mcpData.servers || {}).filter(name => { + const server = mcpData.servers[name]; + // 只包含已连接且已启用的MCP + return server.status === 'connected' && + (server.config.external_mcp_enable || (server.config.enabled && !server.config.disabled)); + }); + } + } catch (mcpError) { + console.warn('加载外部MCP列表失败:', mcpError); + externalMcpNames = []; + } + while (page <= totalPages && page <= 20) { const response = await apiFetch(`/api/config/tools?page=${page}&page_size=${pageSize}`); if (!response.ok) { @@ -383,15 +401,47 @@ function updateMentionCandidates() { let filtered = mentionTools; if (normalizedQuery) { - filtered = mentionTools.filter(tool => { - const nameMatch = tool.name.toLowerCase().includes(normalizedQuery); - const descMatch = tool.description && tool.description.toLowerCase().includes(normalizedQuery); - return nameMatch || descMatch; - }); + // 检查是否精确匹配外部MCP名称 + const exactMatchedMcp = externalMcpNames.find(mcpName => + mcpName.toLowerCase() === normalizedQuery + ); + + if (exactMatchedMcp) { + // 如果完全匹配MCP名称,只显示该MCP下的所有工具 + filtered = mentionTools.filter(tool => { + return tool.externalMcp && tool.externalMcp.toLowerCase() === exactMatchedMcp.toLowerCase(); + }); + } else { + // 检查是否部分匹配MCP名称 + const partialMatchedMcps = externalMcpNames.filter(mcpName => + mcpName.toLowerCase().includes(normalizedQuery) + ); + + // 正常匹配:按工具名称和描述过滤,同时也匹配MCP名称 + filtered = mentionTools.filter(tool => { + const nameMatch = tool.name.toLowerCase().includes(normalizedQuery); + const descMatch = tool.description && tool.description.toLowerCase().includes(normalizedQuery); + const mcpMatch = tool.externalMcp && tool.externalMcp.toLowerCase().includes(normalizedQuery); + + // 如果部分匹配到MCP名称,也包含该MCP下的所有工具 + const mcpPartialMatch = partialMatchedMcps.some(mcpName => + tool.externalMcp && tool.externalMcp.toLowerCase() === mcpName.toLowerCase() + ); + + return nameMatch || descMatch || mcpMatch || mcpPartialMatch; + }); + } } filtered = filtered.slice().sort((a, b) => { if (normalizedQuery) { + // 精确匹配MCP名称的工具优先显示 + const aMcpExact = a.externalMcp && a.externalMcp.toLowerCase() === normalizedQuery; + const bMcpExact = b.externalMcp && b.externalMcp.toLowerCase() === normalizedQuery; + if (aMcpExact !== bMcpExact) { + return aMcpExact ? -1 : 1; + } + const aStarts = a.name.toLowerCase().startsWith(normalizedQuery); const bStarts = b.name.toLowerCase().startsWith(normalizedQuery); if (aStarts !== bStarts) { @@ -1567,10 +1617,12 @@ async function loadConversation(conversationId) { } // 删除对话 -async function deleteConversation(conversationId) { - // 确认删除 - if (!confirm('确定要删除这个对话吗?此操作不可恢复。')) { - return; +async function deleteConversation(conversationId, skipConfirm = false) { + // 确认删除(如果调用者没有跳过确认) + if (!skipConfirm) { + if (!confirm('确定要删除这个对话吗?此操作不可恢复。')) { + return; + } } try { @@ -4603,7 +4655,7 @@ function deleteConversationFromContext() { if (!convId) return; if (confirm('确定要删除此对话吗?')) { - deleteConversation(convId); + deleteConversation(convId, true); // 跳过内部确认,因为这里已经确认过了 } closeContextMenu(); } @@ -4741,7 +4793,7 @@ async function deleteSelectedConversations() { try { for (const id of ids) { - await deleteConversation(id); + await deleteConversation(id, true); // 跳过内部确认,因为批量删除时已经确认过了 } closeBatchManageModal(); loadConversationsWithGroups(); diff --git a/web/static/js/settings.js b/web/static/js/settings.js index 3522ee8a..62fc17e5 100644 --- a/web/static/js/settings.js +++ b/web/static/js/settings.js @@ -294,8 +294,14 @@ function renderToolsList() { external_mcp: tool.external_mcp || '' }; - // 外部工具标签 - const externalBadge = toolState.is_external ? '外部' : ''; + // 外部工具标签,显示来源信息 + let externalBadge = ''; + if (toolState.is_external) { + const externalMcpName = toolState.external_mcp || ''; + const badgeText = externalMcpName ? `外部 (${escapeHtml(externalMcpName)})` : '外部'; + const badgeTitle = externalMcpName ? `外部MCP工具 - 来源:${escapeHtml(externalMcpName)}` : '外部MCP工具'; + externalBadge = `${badgeText}`; + } toolItem.innerHTML = `