From 311ce2e4bc60057d3130b6d24b99184d4e432e93 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Thu, 8 Jan 2026 22:37:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=8E=A8=E8=8D=90=20?= =?UTF-8?q?MCP=20=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=8A=9F=E8=83=BD=E5=92=8C?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E5=90=91=E5=AF=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard-js/components/mcp-manager.js | 310 ++++++++++++++++++ ccw/src/templates/dashboard-js/i18n.js | 24 ++ .../dashboard-js/views/mcp-manager.js | 61 ++++ 3 files changed, 395 insertions(+) diff --git a/ccw/src/templates/dashboard-js/components/mcp-manager.js b/ccw/src/templates/dashboard-js/components/mcp-manager.js index f4bbf1ae..15bfc957 100644 --- a/ccw/src/templates/dashboard-js/components/mcp-manager.js +++ b/ccw/src/templates/dashboard-js/components/mcp-manager.js @@ -1201,6 +1201,310 @@ function setPreferredProjectConfigType(type) { } } +// ========== Recommended MCP Servers ========== +// Pre-configured MCP server definitions for easy installation + +const RECOMMENDED_MCP_SERVERS = [ + { + id: 'ace-tool', + name: 'ACE Tool', + description: 'Augment Context Engine - Semantic code search with real-time codebase indexing', + icon: 'search-code', + category: 'search', + fields: [ + { + key: 'baseUrl', + label: 'Base URL', + type: 'text', + default: 'https://acemcp.heroman.wtf/relay/', + placeholder: 'https://acemcp.heroman.wtf/relay/', + required: true, + description: 'ACE MCP relay server URL' + }, + { + key: 'token', + label: 'API Token', + type: 'password', + default: '', + placeholder: 'ace_xxxxxxxxxxxxxxxx', + required: true, + description: 'Your ACE API token (get from ACE dashboard)' + } + ], + buildConfig: (values) => ({ + command: 'npx', + args: [ + 'ace-tool', + '--base-url', + values.baseUrl || 'https://acemcp.heroman.wtf/relay/', + '--token', + values.token + ] + }) + }, + { + id: 'chrome-devtools', + name: 'Chrome DevTools', + description: 'Browser automation and DevTools integration for web development', + icon: 'chrome', + category: 'browser', + fields: [], + buildConfig: () => ({ + type: 'stdio', + command: 'npx', + args: ['chrome-devtools-mcp@latest'], + env: {} + }) + }, + { + id: 'exa', + name: 'Exa Search', + description: 'AI-powered web search with real-time crawling and content extraction', + icon: 'globe-2', + category: 'search', + fields: [ + { + key: 'apiKey', + label: 'EXA API Key', + type: 'password', + default: '', + placeholder: 'your-exa-api-key', + required: true, + description: 'Get your API key from exa.ai dashboard' + } + ], + buildConfig: (values) => ({ + command: 'npx', + args: ['-y', 'exa-mcp-server'], + env: { + EXA_API_KEY: values.apiKey + } + }) + } +]; + +// Get recommended MCP servers list +function getRecommendedMcpServers() { + return RECOMMENDED_MCP_SERVERS; +} + +// Check if a recommended MCP is already installed +function isRecommendedMcpInstalled(mcpId) { + // Check in current project servers + const currentPath = projectPath; + const projectData = mcpAllProjects[currentPath] || {}; + const projectServers = projectData.mcpServers || {}; + + if (projectServers[mcpId]) return { installed: true, scope: 'project' }; + + // Check in global servers + if (mcpUserServers && mcpUserServers[mcpId]) return { installed: true, scope: 'global' }; + + // Check in Codex servers + if (codexMcpServers && codexMcpServers[mcpId]) return { installed: true, scope: 'codex' }; + + return { installed: false, scope: null }; +} + +// Open recommended MCP install wizard modal +function openRecommendedMcpWizard(mcpId) { + const mcpDef = RECOMMENDED_MCP_SERVERS.find(m => m.id === mcpId); + if (!mcpDef) { + showRefreshToast(`Unknown MCP: ${mcpId}`, 'error'); + return; + } + + // Create wizard modal + const existingModal = document.getElementById('recommendedMcpWizardModal'); + if (existingModal) { + existingModal.remove(); + } + + const hasFields = mcpDef.fields && mcpDef.fields.length > 0; + + const modal = document.createElement('div'); + modal.id = 'recommendedMcpWizardModal'; + modal.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50'; + modal.innerHTML = ` +
+ +
+
+
+ +
+
+

${t('mcp.wizard.install')} ${escapeHtml(mcpDef.name)}

+

${escapeHtml(mcpDef.description)}

+
+
+ +
+ + +
+ ${hasFields ? ` +
+ ${mcpDef.fields.map(field => ` +
+ + ${field.description ? `

${escapeHtml(field.description)}

` : ''} + +
+ `).join('')} +
+ ` : ` +
+ +

${t('mcp.wizard.noConfig')}

+
+ `} + + +
+ +
+ + + +
+
+
+ + +
+ + +
+
+ `; + + document.body.appendChild(modal); + + // Initialize Lucide icons in modal + if (typeof lucide !== 'undefined') { + lucide.createIcons(); + } + + // Set default scope to global + window.selectedWizardScope = 'global'; + + // Focus first input if exists + if (hasFields) { + const firstInput = modal.querySelector('input'); + if (firstInput) firstInput.focus(); + } +} + +// Close recommended MCP wizard modal +function closeRecommendedMcpWizard() { + const modal = document.getElementById('recommendedMcpWizardModal'); + if (modal) { + modal.remove(); + } +} + +// Select scope in wizard +function selectWizardScope(scope) { + window.selectedWizardScope = scope; + + // Update button states + const buttons = document.querySelectorAll('.wizard-scope-btn'); + buttons.forEach(btn => { + if (btn.dataset.scope === scope) { + btn.classList.add('bg-primary/10', 'border-primary'); + } else { + btn.classList.remove('bg-primary/10', 'border-primary'); + } + }); +} + +// Submit recommended MCP wizard +async function submitRecommendedMcpWizard(mcpId) { + const mcpDef = RECOMMENDED_MCP_SERVERS.find(m => m.id === mcpId); + if (!mcpDef) { + showRefreshToast(`Unknown MCP: ${mcpId}`, 'error'); + return; + } + + // Collect field values + const values = {}; + let hasError = false; + + for (const field of mcpDef.fields) { + const input = document.getElementById(`wizard-field-${field.key}`); + const value = input ? input.value.trim() : ''; + + if (field.required && !value) { + showRefreshToast(`${field.label} is required`, 'error'); + if (input) input.focus(); + hasError = true; + break; + } + + values[field.key] = value; + } + + if (hasError) return; + + // Build config + const serverConfig = mcpDef.buildConfig(values); + const scope = window.selectedWizardScope || 'global'; + + try { + showRefreshToast(`Installing ${mcpDef.name}...`, 'info'); + + if (scope === 'codex') { + await addCodexMcpServer(mcpId, serverConfig); + } else if (scope === 'global') { + await addGlobalMcpServer(mcpId, serverConfig); + } else { + await copyMcpServerToProject(mcpId, serverConfig); + } + + closeRecommendedMcpWizard(); + showRefreshToast(`${mcpDef.name} installed successfully`, 'success'); + } catch (err) { + console.error(`Failed to install ${mcpDef.name}:`, err); + showRefreshToast(`Failed to install ${mcpDef.name}: ${err.message}`, 'error'); + } +} + // ========== Global Exports for onclick handlers ========== // Expose functions to global scope to support inline onclick handlers window.setCliMode = setCliMode; @@ -1212,3 +1516,9 @@ window.toggleProjectConfigType = toggleProjectConfigType; window.getPreferredProjectConfigType = getPreferredProjectConfigType; window.setPreferredProjectConfigType = setPreferredProjectConfigType; window.setCcwProjectRootToCurrent = setCcwProjectRootToCurrent; +window.getRecommendedMcpServers = getRecommendedMcpServers; +window.isRecommendedMcpInstalled = isRecommendedMcpInstalled; +window.openRecommendedMcpWizard = openRecommendedMcpWizard; +window.closeRecommendedMcpWizard = closeRecommendedMcpWizard; +window.selectWizardScope = selectWizardScope; +window.submitRecommendedMcpWizard = submitRecommendedMcpWizard; diff --git a/ccw/src/templates/dashboard-js/i18n.js b/ccw/src/templates/dashboard-js/i18n.js index ea0d4907..83696e81 100644 --- a/ccw/src/templates/dashboard-js/i18n.js +++ b/ccw/src/templates/dashboard-js/i18n.js @@ -897,6 +897,18 @@ const i18n = { 'mcp.toProject': 'To Project', 'mcp.toGlobal': 'To Global', + // Recommended MCP + 'mcp.recommended': 'Recommended MCP', + 'mcp.quickSetup': 'Quick Setup', + 'mcp.configRequired': 'config required', + 'mcp.noConfigNeeded': 'No config needed', + 'mcp.reconfigure': 'Configure', + 'mcp.wizard.install': 'Install', + 'mcp.wizard.noConfig': 'No configuration required. Ready to install!', + 'mcp.wizard.installTo': 'Install to', + 'mcp.wizard.project': 'Project', + 'mcp.wizard.global': 'Global', + // MCP CLI Mode 'mcp.cliMode': 'CLI Mode', 'mcp.claudeMode': 'Claude Mode', @@ -2947,6 +2959,18 @@ const i18n = { 'mcp.noTemplatesDesc': '从现有服务器创建模板或添加新模板', 'mcp.templatesDesc': '浏览并安装预配置的 MCP 服务器模板', + // Recommended MCP + 'mcp.recommended': '推荐 MCP', + 'mcp.quickSetup': '快速安装', + 'mcp.configRequired': '需配置', + 'mcp.noConfigNeeded': '无需配置', + 'mcp.reconfigure': '配置', + 'mcp.wizard.install': '安装', + 'mcp.wizard.noConfig': '无需配置,可直接安装!', + 'mcp.wizard.installTo': '安装到', + 'mcp.wizard.project': '项目', + 'mcp.wizard.global': '全局', + // MCP CLI Mode 'mcp.cliMode': 'CLI 模式', 'mcp.claudeMode': 'Claude 模式', diff --git a/ccw/src/templates/dashboard-js/views/mcp-manager.js b/ccw/src/templates/dashboard-js/views/mcp-manager.js index af4302c4..40dfe2d6 100644 --- a/ccw/src/templates/dashboard-js/views/mcp-manager.js +++ b/ccw/src/templates/dashboard-js/views/mcp-manager.js @@ -549,6 +549,67 @@ async function renderMcpManager() { + +
+
+
+
+ +

${t('mcp.recommended')}

+
+ + ${t('mcp.quickSetup')} + +
+
+ +
+ ${getRecommendedMcpServers().map(mcp => { + const installStatus = isRecommendedMcpInstalled(mcp.id); + return ` + + `; + }).join('')} +
+
+