Skip to main content
Glama

Token Saver MCP

by jerry426
dashboard.htmlβ€’20 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Token Saver MCP - Developer Dashboard</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background: #6B5B95; /* Solid color instead of gradient for better compression */ min-height: 100vh; padding: 20px; } .container { max-width: 1400px; margin: 0 auto; } h1 { color: white; text-align: center; margin-bottom: 30px; font-size: 2.5em; /* text-shadow removed for better compression */ } .dashboard { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: auto auto auto; gap: 20px; } .card.full-width { grid-column: 1 / -1; } .card { background: white; border-radius: 10px; padding: 20px; border: 1px solid #ddd; /* Simple border instead of shadow */ transition: transform 0.3s ease; } .card:hover { transform: translateY(-5px); } .card h2 { color: #333; margin-bottom: 15px; font-size: 1.3em; border-bottom: 2px solid #6B5B95; padding-bottom: 10px; } .status-indicator { display: inline-block; width: 12px; height: 12px; border-radius: 50%; margin-right: 8px; animation: pulse 2s infinite; } .status-active { background: #10b981; } .status-inactive { background: #ef4444; } .status-warning { background: #f59e0b; } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } } .metric { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #f0f0f0; } .metric:last-child { border-bottom: none; } .metric-label { color: #666; font-size: 0.9em; } .metric-value { color: #333; font-weight: bold; font-family: 'Courier New', monospace; } .activity-log { max-height: 300px; overflow-y: auto; font-family: 'Courier New', monospace; font-size: 0.85em; background: #f8f9fa; padding: 10px; border-radius: 5px; } .log-entry { padding: 4px 0; border-bottom: 1px solid #e0e0e0; } .log-time { color: #666; margin-right: 10px; } .log-method { color: #6B5B95; font-weight: bold; } .refresh-btn { background: #6B5B95; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 1em; transition: background 0.3s ease; } .refresh-btn:hover { background: #5a4a8a; } .error-message { background: #fee; color: #c00; padding: 10px; border-radius: 5px; margin-top: 10px; } .tools-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; margin-top: 10px; } @media (max-width: 1200px) { .tools-grid { grid-template-columns: repeat(4, 1fr); } } @media (max-width: 900px) { .tools-grid { grid-template-columns: repeat(3, 1fr); } } @media (max-width: 600px) { .tools-grid { grid-template-columns: repeat(2, 1fr); } } .tool-badge { background: #f0f0f0; padding: 5px 10px; border-radius: 20px; font-size: 0.85em; text-align: center; } .tool-badge.lsp { background: #e0f2fe; color: #0369a1; } .tool-badge.cdp { background: #fef3c7; color: #92400e; } .tool-badge.helper { background: #f0fdf4; color: #166534; } .tool-badge.system { background: #e0e7ff; color: #3730a3; } .chart-container { height: 200px; margin-top: 15px; position: relative; } .workspace-info { background: #f8f9fa; padding: 10px; border-radius: 5px; margin-top: 10px; } .workspace-path { font-family: 'Courier New', monospace; font-size: 0.9em; color: #666; word-break: break-all; } </style> </head> <body> <div class="container"> <h1>πŸš€ Token Saver MCP Dashboard</h1> <div class="dashboard"> <!-- Top Row: Server Status | Request Metrics | Token Savings --> <!-- Server Status Card --> <div class="card"> <h2>Server Status</h2> <div class="metric"> <span class="metric-label">Status</span> <span class="metric-value"> <span class="status-indicator status-active"></span> <span id="server-status">Active</span> </span> </div> <div class="metric"> <span class="metric-label">Port</span> <span class="metric-value" id="server-port">9527</span> </div> <div class="metric"> <span class="metric-label">Uptime</span> <span class="metric-value" id="server-uptime">0h 0m</span> </div> <div class="metric"> <span class="metric-label">Mode</span> <span class="metric-value" id="server-mode">Sessionless</span> </div> <div class="workspace-info"> <div class="metric-label">Workspace</div> <div class="workspace-path" id="workspace-path">Loading...</div> </div> </div> <!-- Request Metrics Card --> <div class="card"> <h2>Request Metrics</h2> <div class="metric"> <span class="metric-label">Total Requests</span> <span class="metric-value" id="total-requests">0</span> </div> <div class="metric"> <span class="metric-label">Success Rate</span> <span class="metric-value" id="success-rate">100%</span> </div> <div class="metric"> <span class="metric-label">Avg Response Time</span> <span class="metric-value" id="avg-response-time">0ms</span> </div> <div class="metric"> <span class="metric-label">Active Connections</span> <span class="metric-value" id="active-connections">0</span> </div> <div class="metric"> <span class="metric-label">Errors (24h)</span> <span class="metric-value" id="error-count">0</span> </div> <button class="refresh-btn" onclick="refreshMetrics()">Refresh</button> </div> <!-- Token Savings Card --> <div class="card"> <h2>Token Savings</h2> <div class="metric"> <span class="metric-label">Total Saved</span> <span class="metric-value" id="total-tokens-saved">0</span> </div> <div class="metric"> <span class="metric-label">Avg per Request</span> <span class="metric-value" id="avg-tokens-saved">0</span> </div> <div class="metric"> <span class="metric-label">Last 24h</span> <span class="metric-value" id="tokens-saved-24h">0</span> </div> <div class="metric"> <span class="metric-label">Cost Saved</span> <span class="metric-value" id="cost-saved">$0.00</span> </div> <div class="metric"> <span class="metric-label">Efficiency</span> <span class="metric-value" id="efficiency">100x</span> </div> </div> <!-- Middle Row: Available Tools (full-width) --> <!-- Available Tools Card --> <div class="card full-width"> <h2>Available Tools</h2> <div class="metric"> <span class="metric-label">LSP Tools</span> <span class="metric-value" id="lsp-tool-count">15</span> </div> <div class="metric"> <span class="metric-label">CDP Tools</span> <span class="metric-value" id="cdp-tool-count">8</span> </div> <div class="metric"> <span class="metric-label">Helper Tools</span> <span class="metric-value" id="helper-tool-count">5</span> </div> <div class="metric"> <span class="metric-label">System Tools</span> <span class="metric-value" id="system-tool-count">4</span> </div> <div class="tools-grid" id="tools-list"> <!-- Tools will be populated here --> </div> </div> <!-- Bottom Row: Recent Activity | Response Time Graph | Most Used Tools --> <!-- Recent Activity Card --> <div class="card"> <h2>Recent Activity</h2> <div class="activity-log" id="activity-log"> <!-- Activity entries will be added here --> </div> </div> <!-- Performance Chart Card --> <div class="card"> <h2>Response Time (Last 50 requests)</h2> <div class="chart-container"> <canvas id="performance-chart"></canvas> </div> </div> <!-- Popular Tools Card --> <div class="card"> <h2>Most Used Tools (24h)</h2> <div id="popular-tools"> <!-- Will be populated with tool usage stats --> </div> </div> </div> </div> <script> let eventSource = null; let startTime = Date.now(); let responseTimeHistory = []; async function fetchMetrics() { try { const response = await fetch('/metrics'); const data = await response.json(); updateDashboard(data); } catch (error) { console.error('Failed to fetch metrics:', error); document.getElementById('server-status').textContent = 'Error'; document.querySelector('.status-indicator').className = 'status-indicator status-inactive'; } } async function fetchWorkspaceInfo() { try { const response = await fetch('/workspace-info'); const data = await response.json(); document.getElementById('workspace-path').textContent = data.workspacePath || 'No workspace'; document.getElementById('server-port').textContent = data.port || '9527'; } catch (error) { console.error('Failed to fetch workspace info:', error); } } async function fetchTools() { try { const response = await fetch('/available-tools'); const data = await response.json(); const toolsList = document.getElementById('tools-list'); toolsList.innerHTML = ''; data.tools.forEach(tool => { const badge = document.createElement('div'); badge.className = `tool-badge ${tool.displayCategory || tool.category}`; badge.textContent = tool.callCount > 0 ? `${tool.name} (${tool.callCount})` : tool.name; toolsList.appendChild(badge); }); document.getElementById('lsp-tool-count').textContent = data.tools.filter(t => t.category === 'lsp').length; document.getElementById('cdp-tool-count').textContent = data.tools.filter(t => t.category === 'cdp').length; document.getElementById('helper-tool-count').textContent = data.tools.filter(t => t.category === 'helper').length; document.getElementById('system-tool-count').textContent = data.tools.filter(t => t.category === 'system').length; } catch (error) { console.error('Failed to fetch tools:', error); } } function updateDashboard(data) { // Update metrics document.getElementById('total-requests').textContent = data.totalRequests || 0; document.getElementById('success-rate').textContent = Math.round((data.successRate || 0) * 100) + '%'; document.getElementById('avg-response-time').textContent = Math.round(data.avgResponseTime || 0) + 'ms'; document.getElementById('active-connections').textContent = data.activeConnections || 0; document.getElementById('error-count').textContent = data.errorCount || 0; // Update token savings metrics document.getElementById('total-tokens-saved').textContent = (data.totalTokensSaved || 0).toLocaleString(); document.getElementById('avg-tokens-saved').textContent = Math.round(data.avgTokensSaved || 0).toLocaleString(); document.getElementById('tokens-saved-24h').textContent = (data.tokensSaved24h || 0).toLocaleString(); // Calculate cost saved (assuming $0.01 per 1000 tokens) const costSaved = ((data.totalTokensSaved || 0) / 1000) * 0.01; document.getElementById('cost-saved').textContent = '$' + costSaved.toFixed(2); // Calculate efficiency const efficiency = data.efficiency || 100; document.getElementById('efficiency').textContent = efficiency + 'x'; // Update uptime const uptime = Date.now() - startTime; const hours = Math.floor(uptime / 3600000); const minutes = Math.floor((uptime % 3600000) / 60000); document.getElementById('server-uptime').textContent = `${hours}h ${minutes}m`; // Update activity log if (data.recentActivity) { const logContainer = document.getElementById('activity-log'); logContainer.innerHTML = data.recentActivity.map(entry => ` <div class="log-entry"> <span class="log-time">${new Date(entry.timestamp).toLocaleTimeString()}</span> <span class="log-method">${entry.method}</span> ${entry.tool ? `<span>${entry.tool}</span>` : ''} <span>${entry.status}</span> </div> `).join(''); } // Update popular tools if (data.popularTools) { const popularTools = document.getElementById('popular-tools'); popularTools.innerHTML = data.popularTools.map((tool, index) => ` <div class="metric"> <span class="metric-label">${index + 1}. ${tool.name}</span> <span class="metric-value">${tool.count} calls</span> </div> `).join(''); } // Update response time history for chart if (data.responseTimeHistory) { responseTimeHistory = data.responseTimeHistory; updateChart(); } } function updateChart() { const canvas = document.getElementById('performance-chart'); const ctx = canvas.getContext('2d'); const width = canvas.offsetWidth; const height = canvas.offsetHeight; canvas.width = width; canvas.height = height; if (responseTimeHistory.length === 0) return; const maxTime = Math.max(...responseTimeHistory, 100); const step = width / Math.max(responseTimeHistory.length - 1, 1); ctx.strokeStyle = '#667eea'; ctx.lineWidth = 2; ctx.beginPath(); responseTimeHistory.forEach((time, index) => { const x = index * step; const y = height - (time / maxTime) * height; if (index === 0) { ctx.moveTo(x, y); } else { ctx.lineTo(x, y); } }); ctx.stroke(); } function connectSSE() { eventSource = new EventSource('/dashboard-events'); eventSource.onmessage = function(event) { const data = JSON.parse(event.data); updateDashboard(data); }; eventSource.onerror = function(error) { console.error('SSE connection error:', error); setTimeout(connectSSE, 5000); // Reconnect after 5 seconds }; } function refreshMetrics() { fetchMetrics(); fetchTools(); } // Initialize dashboard document.addEventListener('DOMContentLoaded', function() { fetchMetrics(); fetchWorkspaceInfo(); fetchTools(); connectSSE(); // Auto-refresh every 5 seconds setInterval(() => { fetchMetrics(); fetchTools(); // Also refresh tools to update call counts }, 5000); }); </script> </body> </html>

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jerry426/token-saver-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server