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;