UNPKG

bmad-method-mcp

Version:

Breakthrough Method of Agile AI-driven Development with Enhanced MCP Integration

451 lines (395 loc) 15 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>BMad MCP Server Dashboard</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0f172a; color: #e2e8f0; line-height: 1.6; } .container { max-width: 1200px; margin: 0 auto; padding: 20px; } .header { text-align: center; margin-bottom: 30px; padding: 20px; background: linear-gradient(135deg, #1e293b 0%, #334155 100%); border-radius: 12px; border: 1px solid #475569; } .header h1 { color: #60a5fa; margin-bottom: 8px; font-size: 2.2rem; } .header p { color: #94a3b8; font-size: 1.1rem; } .status-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin-bottom: 30px; } .status-card { background: #1e293b; padding: 20px; border-radius: 12px; border: 1px solid #475569; transition: transform 0.2s ease, border-color 0.2s ease; } .status-card:hover { transform: translateY(-2px); border-color: #60a5fa; } .status-card h3 { color: #60a5fa; margin-bottom: 12px; font-size: 1.2rem; } .status-value { font-size: 2rem; font-weight: bold; margin: 8px 0; } .status-healthy { color: #10b981; } .status-warning { color: #f59e0b; } .status-error { color: #ef4444; } .actions { display: flex; gap: 15px; margin-bottom: 30px; flex-wrap: wrap; } .btn { padding: 12px 24px; border: none; border-radius: 8px; cursor: pointer; font-size: 1rem; font-weight: 600; transition: all 0.2s ease; text-decoration: none; display: inline-flex; align-items: center; gap: 8px; } .btn-primary { background: #3b82f6; color: white; } .btn-primary:hover { background: #2563eb; transform: translateY(-1px); } .btn-danger { background: #ef4444; color: white; } .btn-danger:hover { background: #dc2626; transform: translateY(-1px); } .btn-secondary { background: #475569; color: #e2e8f0; } .btn-secondary:hover { background: #64748b; } .logs-section { background: #1e293b; border-radius: 12px; border: 1px solid #475569; overflow: hidden; } .logs-header { background: #334155; padding: 15px 20px; border-bottom: 1px solid #475569; display: flex; justify-content: between; align-items: center; } .logs-header h3 { color: #60a5fa; margin: 0; } .logs-container { height: 400px; overflow-y: auto; padding: 20px; font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; font-size: 14px; background: #0f172a; } .log-entry { margin-bottom: 8px; padding: 4px 0; border-left: 3px solid transparent; padding-left: 8px; } .log-info { border-left-color: #3b82f6; color: #93c5fd; } .log-warn { border-left-color: #f59e0b; color: #fbbf24; } .log-error { border-left-color: #ef4444; color: #fca5a5; } .log-debug { border-left-color: #8b5cf6; color: #c4b5fd; } .log-timestamp { color: #64748b; font-size: 12px; } .footer { text-align: center; margin-top: 30px; padding: 20px; color: #64748b; border-top: 1px solid #475569; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .status-indicator { display: inline-block; width: 12px; height: 12px; border-radius: 50%; margin-right: 8px; } .status-indicator.running { background: #10b981; animation: pulse 2s infinite; } .status-indicator.stopped { background: #ef4444; } </style> </head> <body> <div class="container"> <div class="header"> <h1>🚀 BMad MCP Server Dashboard</h1> <p>Monitoring and Management Interface</p> </div> <div class="status-grid"> <div class="status-card"> <h3>🔗 Server Status</h3> <div class="status-value status-healthy" id="server-status"> <span class="status-indicator running"></span> Running </div> <p>MCP Server is active and responding</p> </div> <div class="status-card"> <h3>📊 Active Sessions</h3> <div class="status-value" id="active-sessions">0</div> <p>Connected AI agents</p> </div> <div class="status-card"> <h3>⚡ Tool Calls</h3> <div class="status-value" id="tool-calls">0</div> <p>Total MCP tool executions</p> </div> <div class="status-card"> <h3>🗄️ Database</h3> <div class="status-value status-healthy" id="db-status">Connected</div> <p>SQLite database status</p> </div> </div> <div class="actions"> <button class="btn btn-primary" onclick="refreshStats()"> 🔄 Refresh Stats </button> <button class="btn btn-secondary" onclick="exportLogs()"> 📥 Export Logs </button> <button class="btn btn-secondary" onclick="clearLogs()"> 🗑️ Clear Logs </button> <button class="btn btn-danger" onclick="shutdownServer()" id="shutdown-btn"> 🛑 Shutdown Server </button> </div> <div class="logs-section"> <div class="logs-header"> <h3>📝 Server Logs</h3> <small>Real-time server activity</small> </div> <div class="logs-container" id="logs-container"> <div class="log-entry log-info"> <span class="log-timestamp">[Starting up...]</span> Connecting to BMad MCP Server... </div> </div> </div> <div class="footer"> <p>BMad Method MCP Server Dashboard | Port: <span id="server-port">3001</span> | PID: <span id="server-pid">---</span></p> </div> </div> <script> let logCount = 0; let toolCallCount = 0; let activeSessions = 0; // Initialize dashboard document.addEventListener('DOMContentLoaded', function() { initializeDashboard(); startLogPolling(); startStatsPolling(); }); function initializeDashboard() { // Get server info fetch('/api/dashboard/info') .then(response => response.json()) .then(data => { document.getElementById('server-port').textContent = data.port || '3001'; document.getElementById('server-pid').textContent = data.pid || process.pid; }) .catch(error => { console.log('Dashboard API not available yet'); }); } function startLogPolling() { // Poll for new logs every 2 seconds setInterval(fetchLogs, 2000); } function startStatsPolling() { // Poll for stats every 5 seconds setInterval(refreshStats, 5000); } function fetchLogs() { fetch('/api/dashboard/logs') .then(response => response.json()) .then(data => { const logsContainer = document.getElementById('logs-container'); if (data.logs && data.logs.length > 0) { // Clear existing logs if we have new ones if (data.logs.length !== logCount) { logsContainer.innerHTML = ''; data.logs.forEach(log => { const logEntry = document.createElement('div'); logEntry.className = `log-entry log-${log.level}`; logEntry.innerHTML = ` <span class="log-timestamp">[${log.timestamp}]</span> ${log.message} `; logsContainer.appendChild(logEntry); }); // Scroll to bottom logsContainer.scrollTop = logsContainer.scrollHeight; logCount = data.logs.length; } } }) .catch(error => { // Dashboard not ready yet, add placeholder log if (logCount === 0) { addLogEntry('info', 'Waiting for MCP server connection...'); logCount = 1; } }); } function refreshStats() { fetch('/api/dashboard/stats') .then(response => response.json()) .then(data => { document.getElementById('active-sessions').textContent = data.activeSessions || 0; document.getElementById('tool-calls').textContent = data.toolCalls || 0; // Update server status const serverStatus = document.getElementById('server-status'); if (data.healthy) { serverStatus.innerHTML = '<span class="status-indicator running"></span>Running'; serverStatus.className = 'status-value status-healthy'; } else { serverStatus.innerHTML = '<span class="status-indicator stopped"></span>Error'; serverStatus.className = 'status-value status-error'; } }) .catch(error => { console.log('Stats not available:', error); }); } function addLogEntry(level, message) { const logsContainer = document.getElementById('logs-container'); const logEntry = document.createElement('div'); logEntry.className = `log-entry log-${level}`; logEntry.innerHTML = ` <span class="log-timestamp">[${new Date().toLocaleTimeString()}]</span> ${message} `; logsContainer.appendChild(logEntry); logsContainer.scrollTop = logsContainer.scrollHeight; } function exportLogs() { fetch('/api/dashboard/logs/export') .then(response => response.blob()) .then(blob => { const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `bmad-mcp-logs-${new Date().toISOString().split('T')[0]}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); }) .catch(error => { alert('Export failed: ' + error.message); }); } function clearLogs() { if (confirm('Clear all logs? This action cannot be undone.')) { fetch('/api/dashboard/logs/clear', { method: 'POST' }) .then(response => response.json()) .then(data => { document.getElementById('logs-container').innerHTML = ''; addLogEntry('info', 'Logs cleared'); logCount = 1; }) .catch(error => { alert('Clear failed: ' + error.message); }); } } function shutdownServer() { if (confirm('Shutdown BMad MCP Server? This will close all connections.')) { const shutdownBtn = document.getElementById('shutdown-btn'); shutdownBtn.textContent = '🔄 Shutting down...'; shutdownBtn.disabled = true; fetch('/api/dashboard/shutdown', { method: 'POST' }) .then(response => response.json()) .then(data => { addLogEntry('warn', 'Server shutdown initiated'); // Update UI to show shutdown state setTimeout(() => { document.getElementById('server-status').innerHTML = '<span class="status-indicator stopped"></span>Shutting down'; document.getElementById('server-status').className = 'status-value status-warning'; }, 1000); // Close dashboard after delay setTimeout(() => { window.close(); }, 3000); }) .catch(error => { shutdownBtn.textContent = '🛑 Shutdown Server'; shutdownBtn.disabled = false; alert('Shutdown failed: ' + error.message); }); } } </script> </body> </html>