diff --git a/web/static/css/style.css b/web/static/css/style.css index 96b8f66a..0b525465 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -4472,6 +4472,25 @@ header { flex-wrap: wrap; } +.tool-resident-toggle { + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 0.75rem; + color: var(--text-secondary); + border: 1px solid var(--border-color); + border-radius: 12px; + padding: 2px 8px; + background: var(--bg-secondary); + cursor: pointer; +} + +.tool-resident-toggle input[type="checkbox"] { + width: 14px; + height: 14px; + margin: 0; +} + .external-tool-badge { display: inline-flex; align-items: center; diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index bdce7bd1..905024fa 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -576,6 +576,10 @@ "addExternal": "Add external MCP", "toolConfig": "MCP tool config", "saveToolConfig": "Save tool config", + "alwaysVisibleLabel": "Pinned", + "alwaysVisibleHint": "Always keep visible in Tool Search results", + "alwaysVisibleBuiltinLabel": "Builtin default", + "alwaysVisibleBuiltinHint": "Backend builtin tool is pinned by default and cannot be disabled", "externalConfig": "External MCP config", "loadingTools": "Loading tools...", "loadToolsTimeout": "Tools load timeout. External MCP may be slow. Click Refresh to retry or check connection.", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index b6738db9..905c5c70 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -576,6 +576,10 @@ "addExternal": "添加外部MCP", "toolConfig": "MCP 工具配置", "saveToolConfig": "保存工具配置", + "alwaysVisibleLabel": "常驻", + "alwaysVisibleHint": "始终常驻在 Tool Search 可见列表(不被 tool_search 隐藏)", + "alwaysVisibleBuiltinLabel": "内置默认", + "alwaysVisibleBuiltinHint": "后端内置工具默认常驻,不可关闭", "externalConfig": "外部 MCP 配置", "loadingTools": "正在加载工具列表...", "loadToolsTimeout": "加载工具列表超时,可能是外部MCP连接较慢。请点击\"刷新\"按钮重试,或检查外部MCP连接状态。", diff --git a/web/static/js/router.js b/web/static/js/router.js index 6313cd12..e05f0cde 100644 --- a/web/static/js/router.js +++ b/web/static/js/router.js @@ -301,26 +301,38 @@ async function initPage(pageId) { break; case 'mcp-management': // 初始化MCP管理 + const startLoadMcpTools = () => { + // 加载工具列表(MCP工具配置已移到MCP管理页面) + // 使用异步加载,避免阻塞页面渲染 + if (typeof loadToolsList === 'function') { + // 确保工具分页设置已初始化 + if (typeof getToolsPageSize === 'function' && typeof toolsPagination !== 'undefined') { + toolsPagination.pageSize = getToolsPageSize(); + } + // 延迟加载,让页面先渲染 + setTimeout(() => { + loadToolsList(1, '').catch(err => { + console.error('加载工具列表失败:', err); + }); + }, 100); + } + }; + // 先拉取全局配置,确保 tool_search 常驻状态按后端生效集合展示 + if (typeof loadConfig === 'function') { + loadConfig(false) + .catch(err => { + console.warn('加载配置失败(将继续加载工具列表):', err); + }) + .finally(startLoadMcpTools); + } else { + startLoadMcpTools(); + } // 先加载外部MCP列表(快速),然后加载工具列表 if (typeof loadExternalMCPs === 'function') { loadExternalMCPs().catch(err => { console.warn('加载外部MCP列表失败:', err); }); } - // 加载工具列表(MCP工具配置已移到MCP管理页面) - // 使用异步加载,避免阻塞页面渲染 - if (typeof loadToolsList === 'function') { - // 确保工具分页设置已初始化 - if (typeof getToolsPageSize === 'function' && typeof toolsPagination !== 'undefined') { - toolsPagination.pageSize = getToolsPageSize(); - } - // 延迟加载,让页面先渲染 - setTimeout(() => { - loadToolsList(1, '').catch(err => { - console.error('加载工具列表失败:', err); - }); - }, 100); - } break; case 'vulnerabilities': // 初始化漏洞管理页面 diff --git a/web/static/js/settings.js b/web/static/js/settings.js index 4cd43809..7f52060d 100644 --- a/web/static/js/settings.js +++ b/web/static/js/settings.js @@ -1,6 +1,8 @@ // 设置相关功能 let currentConfig = null; let allTools = []; +let alwaysVisibleToolNames = new Set(); +let alwaysVisibleBuiltinToolNames = new Set(); // 全局工具状态映射,用于保存用户在所有页面的修改 // key: 唯一工具标识符(toolKey),value: { enabled: boolean, is_external: boolean, external_mcp: string } let toolStateMap = new Map(); @@ -100,6 +102,14 @@ async function loadConfig(loadTools = true) { } currentConfig = await response.json(); + const alwaysVisibleList = currentConfig?.multi_agent?.tool_search_always_visible_effective_tools; + const alwaysVisibleConfigured = currentConfig?.multi_agent?.tool_search_always_visible_tools; + alwaysVisibleToolNames = new Set(Array.isArray(alwaysVisibleList) ? alwaysVisibleList.filter(Boolean) : []); + alwaysVisibleBuiltinToolNames = new Set( + alwaysVisibleToolNames.size > 0 && Array.isArray(alwaysVisibleConfigured) + ? Array.from(alwaysVisibleToolNames).filter(name => !alwaysVisibleConfigured.includes(name)) + : [] + ); // 填充OpenAI配置 const providerEl = document.getElementById('openai-provider'); @@ -498,6 +508,8 @@ function renderToolsList() { is_external: tool.is_external || false, external_mcp: tool.external_mcp || '' }; + const alwaysVisibleChecked = alwaysVisibleToolNames.has(tool.name); + const alwaysVisibleLocked = alwaysVisibleBuiltinToolNames.has(tool.name); // 外部工具标签,显示来源信息(可点击跳转到对应 MCP 卡片) let externalBadge = ''; @@ -521,6 +533,11 @@ function renderToolsList() {
${escapeHtml(tool.name)} ${externalBadge} + + ${alwaysVisibleLocked ? `${typeof window.t === 'function' ? window.t('mcp.alwaysVisibleBuiltinLabel') : '内置默认'}` : ''}
${escapeHtml(tool.description || (typeof window.t === 'function' ? window.t('mcp.noDescription') : '无描述'))}
@@ -716,6 +733,16 @@ function handleToolCheckboxChange(toolKey, enabled) { updateToolsStats(); } +function handleToolAlwaysVisibleChange(toolName, alwaysVisible) { + const name = (toolName || '').trim(); + if (!name) return; + if (alwaysVisible) { + alwaysVisibleToolNames.add(name); + } else { + alwaysVisibleToolNames.delete(name); + } +} + // 全选工具 function selectAllTools() { document.querySelectorAll('#tools-list input[type="checkbox"]').forEach(checkbox => { @@ -886,9 +913,11 @@ async function updateToolsStats() { } const tStats = typeof window.t === 'function' ? window.t : (k) => k; + const pinnedCount = alwaysVisibleToolNames.size; statsEl.innerHTML = ` ✅ ${tStats('mcp.currentPageEnabled')}: ${currentPageEnabled} / ${currentPageTotal} 📊 ${tStats('mcp.totalEnabled')}: ${totalEnabled} / ${totalTools} + 📌 ${tStats('mcp.alwaysVisibleLabel')}: ${pinnedCount} `; } @@ -1230,6 +1259,13 @@ async function saveToolsConfig() { const config = { openai: currentConfig.openai || {}, agent: currentConfig.agent || {}, + multi_agent: { + enabled: currentConfig?.multi_agent?.enabled === true, + robot_use_multi_agent: currentConfig?.multi_agent?.robot_use_multi_agent === true, + batch_use_multi_agent: currentConfig?.multi_agent?.batch_use_multi_agent === true, + plan_execute_loop_max_iterations: Number(currentConfig?.multi_agent?.plan_execute_loop_max_iterations || 0), + tool_search_always_visible_tools: Array.from(alwaysVisibleToolNames) + }, tools: [] };