From 8b927f302c67574e227758b58d8882e0483431e5 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Wed, 17 Dec 2025 19:23:39 +0800 Subject: [PATCH] Fix MCP Manager panel - 13 critical issues resolved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issues fixed: 1. API endpoint mismatch (/api/mcp-add-global-server) 2. Undefined function reference (installMcpToProject → copyMcpServerToProject) 3. Inline onclick handler scope issues (converted to data-action) 4. querySelector only finding first element (use querySelectorAll) 5. Path normalization causing wrong MCP badge count 6. Lucide icons destroying event listeners (reordered execution) 7. Codex button JSON serialization syntax error 8. Codex API routes 404 (add /api/codex-mcp route matching) 9. False warning suppression for conditional buttons 10. HTML syntax error from JSON in onclick attributes 11. CSS z-index issue - ::before pseudo-element blocking clicks 12. Navigation badge path matching (try both slash formats) 13. Remove deprecated codex_lens tool (merged into smart_search) Key changes: - server.ts: Add /api/codex-mcp route matching - 15-mcp-manager.css: Add pointer-events:none to ::before, z-index for buttons - components/mcp-manager.js: Fix updateMcpBadge path matching, global exports - views/mcp-manager.js: Convert onclick to data-action, add event listeners, update CCW_MCP_TOOLS list, fix JSON escaping in HTML attributes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- ccw/src/core/server.ts | 4 +- .../dashboard-css/15-mcp-manager.css | 10 ++ .../dashboard-js/components/mcp-manager.js | 21 ++- .../dashboard-js/views/mcp-manager.js | 160 +++++++++++++++--- 4 files changed, 170 insertions(+), 25 deletions(-) diff --git a/ccw/src/core/server.ts b/ccw/src/core/server.ts index 0bd08645..fef8f383 100644 --- a/ccw/src/core/server.ts +++ b/ccw/src/core/server.ts @@ -269,8 +269,8 @@ export async function startServer(options: ServerOptions = {}): Promise
@@ -322,7 +321,9 @@ async function renderMcpManager() {
${!alreadyInCodex ? ` @@ -421,26 +422,26 @@ async function renderMcpManager() {
${isCcwToolsInstalled ? ` ` : `
${!alreadyInClaude ? ` @@ -769,11 +772,12 @@ async function renderMcpManager() { `; - // Attach event listeners for toggle switches - attachMcpEventListeners(); - - // Initialize Lucide icons + // Initialize Lucide icons FIRST (before attaching event listeners) + // lucide.createIcons() may replace DOM elements, which would remove event listeners if (typeof lucide !== 'undefined') lucide.createIcons(); + + // Attach event listeners AFTER icon initialization + attachMcpEventListeners(); } // Render card for Project Available MCP (current project can use) @@ -1040,7 +1044,9 @@ function renderAvailableServerCardForCodex(serverName, serverInfo) { ${!alreadyInCodex ? ` @@ -1066,7 +1072,9 @@ function renderAvailableServerCardForCodex(serverName, serverInfo) {
@@ -1349,7 +1363,7 @@ function attachMcpEventListeners() { btn.addEventListener('click', async (e) => { const serverName = btn.dataset.serverName; const serverConfig = JSON.parse(btn.dataset.serverConfig); - await installMcpToProject(serverName, serverConfig); + await copyMcpServerToProject(serverName, serverConfig); }); }); @@ -1390,6 +1404,51 @@ function attachMcpEventListeners() { }); }); + // ======================================== + // CCW Tools MCP Event Listeners + // ======================================== + + // CCW Tools action buttons (workspace/global install/update) + const ccwActions = { + 'update-ccw-workspace': () => updateCcwToolsMcp('workspace'), + 'update-ccw-global': () => updateCcwToolsMcp('global'), + 'install-ccw-workspace': () => installCcwToolsMcp('workspace'), + 'install-ccw-global': () => installCcwToolsMcp('global'), + 'install-ccw-codex': () => installCcwToolsMcpToCodex() + }; + + // Mode-specific and conditionally rendered actions (don't warn if not found) + const conditionalActions = new Set([ + 'install-ccw-codex', // Only in Codex mode + 'update-ccw-workspace', // Only if ccw-tools installed + 'update-ccw-global' // Only if ccw-tools installed + ]); + + Object.entries(ccwActions).forEach(([action, handler]) => { + const btns = document.querySelectorAll(`button[data-action="${action}"]`); + + if (btns.length > 0) { + console.log(`[MCP] Attaching listener to ${action} (${btns.length} button(s) found)`); + btns.forEach(btn => { + btn.addEventListener('click', async (e) => { + e.preventDefault(); + console.log(`[MCP] Button clicked: ${action}`); + try { + await handler(); + } catch (err) { + console.error(`[MCP] Error executing handler for ${action}:`, err); + if (typeof showRefreshToast === 'function') { + showRefreshToast(`Action failed: ${err.message}`, 'error'); + } + } + }); + }); + } else if (!conditionalActions.has(action)) { + // Only warn if button is not conditionally rendered + console.warn(`[MCP] No buttons found for action: ${action}`); + } + }); + // ======================================== // Codex MCP Event Listeners // ======================================== @@ -1413,6 +1472,61 @@ function attachMcpEventListeners() { }); }); + // Copy Claude servers to Codex + document.querySelectorAll('button[data-action="copy-to-codex"]').forEach(btn => { + btn.addEventListener('click', async (e) => { + e.preventDefault(); + const serverName = btn.dataset.serverName; + const serverConfig = JSON.parse(btn.dataset.serverConfig); + console.log('[MCP] Copying to Codex:', serverName); + await copyClaudeServerToCodex(serverName, serverConfig); + }); + }); + + // Copy Codex servers to Claude + document.querySelectorAll('button[data-action="copy-codex-to-claude"]').forEach(btn => { + btn.addEventListener('click', async (e) => { + e.preventDefault(); + e.stopPropagation(); + const serverName = btn.dataset.serverName; + let serverConfig; + try { + serverConfig = JSON.parse(btn.dataset.serverConfig); + } catch (err) { + console.error('[MCP] JSON Parse Error:', err); + if (typeof showRefreshToast === 'function') { + showRefreshToast('Failed to parse server configuration', 'error'); + } + return; + } + console.log('[MCP] Copying Codex to Claude:', serverName); + await copyCodexServerToClaude(serverName, serverConfig); + }); + }); + + // Copy servers across CLI tools + document.querySelectorAll('button[data-action="copy-cross-cli"]').forEach(btn => { + btn.addEventListener('click', async (e) => { + e.preventDefault(); + e.stopPropagation(); + const serverName = btn.dataset.serverName; + let serverConfig; + try { + serverConfig = JSON.parse(btn.dataset.serverConfig); + } catch (err) { + console.error('[MCP] JSON Parse Error:', err); + if (typeof showRefreshToast === 'function') { + showRefreshToast('Failed to parse server configuration', 'error'); + } + return; + } + const fromCli = btn.dataset.fromCli; + const targetCli = btn.dataset.targetCli; + console.log('[MCP] Copying cross-CLI:', serverName, 'from', fromCli, 'to', targetCli); + await copyCrossCliServer(serverName, serverConfig, fromCli, targetCli); + }); + }); + // View details / Edit - click on Claude server card document.querySelectorAll('.mcp-server-card[data-action="view-details"]').forEach(card => { card.addEventListener('click', (e) => { @@ -1844,7 +1958,7 @@ async function installFromTemplate(templateName, scope = 'project') { // Install based on scope if (scope === 'project') { - await installMcpToProject(serverName, template.serverConfig); + await copyMcpServerToProject(serverName, template.serverConfig); } else if (scope === 'global') { await addGlobalMcpServer(serverName, template.serverConfig); } @@ -1880,3 +1994,11 @@ async function deleteMcpTemplate(templateName) { showRefreshToast(t('mcp.templateDeleteFailed', { error: error.message }), 'error'); } } + +// ========== Global Exports for onclick handlers ========== +// Expose functions to global scope to support inline onclick handlers +window.openCodexMcpCreateModal = openCodexMcpCreateModal; +window.closeMcpEditModal = closeMcpEditModal; +window.saveMcpEdit = saveMcpEdit; +window.deleteMcpFromEdit = deleteMcpFromEdit; +window.saveMcpAsTemplate = saveMcpAsTemplate;