Skip to main content
Glama
index.html16.6 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Logstash MCP Server - Web UI</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); overflow: hidden; } .header { background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%); color: white; padding: 30px; text-align: center; } .header h1 { font-size: 2.5em; margin-bottom: 10px; } .header p { font-size: 1.2em; opacity: 0.9; } .main-content { padding: 30px; } .status-section { background: #f8f9fa; border: 2px solid #dee2e6; border-radius: 8px; padding: 20px; margin-bottom: 30px; } .status-indicator { display: inline-block; width: 12px; height: 12px; border-radius: 50%; margin-right: 8px; } .status-running { background-color: #28a745; } .status-stopped { background-color: #dc3545; } .btn { padding: 12px 24px; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; transition: all 0.3s ease; margin: 5px; } .btn-primary { background-color: #007bff; color: white; } .btn-primary:hover { background-color: #0056b3; } .btn-success { background-color: #28a745; color: white; } .btn-success:hover { background-color: #1e7e34; } .tools-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-top: 30px; } .tool-card { background: white; border: 2px solid #e9ecef; border-radius: 8px; padding: 20px; transition: all 0.3s ease; } .tool-card:hover { border-color: #007bff; box-shadow: 0 4px 12px rgba(0,123,255,0.2); } .tool-card h3 { color: #2c3e50; margin-bottom: 10px; font-size: 1.3em; } .tool-card p { color: #6c757d; margin-bottom: 15px; line-height: 1.5; } .tool-inputs { margin: 15px 0; } .tool-inputs input, .tool-inputs select { width: 100%; padding: 8px 12px; border: 1px solid #ced4da; border-radius: 4px; margin: 5px 0; } .output-section { margin-top: 30px; } .output-box { background: #2d3748; color: #e2e8f0; border-radius: 8px; padding: 20px; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 14px; line-height: 1.6; max-height: 500px; overflow-y: auto; white-space: pre-wrap; word-wrap: break-word; } .loading { text-align: center; padding: 20px; color: #6c757d; } .loading::after { content: ''; display: inline-block; width: 20px; height: 20px; border: 3px solid #f3f3f3; border-top: 3px solid #007bff; border-radius: 50%; animation: spin 1s linear infinite; margin-left: 10px; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .alert { padding: 15px; border-radius: 6px; margin: 15px 0; } .alert-success { background-color: #d4edda; border: 1px solid #c3e6cb; color: #155724; } .alert-danger { background-color: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; } .section-title { font-size: 1.5em; color: #2c3e50; margin-bottom: 20px; border-bottom: 2px solid #e9ecef; padding-bottom: 10px; } </style> </head> <body> <div class="container"> <div class="header"> <h1>🚀 Logstash MCP Server</h1> <p>Web Interface for Logstash Monitoring</p> </div> <div class="main-content"> <!-- Server Status Section --> <div class="status-section"> <h2 class="section-title">Server Status</h2> <div id="server-status"> <span class="status-indicator status-stopped"></span> <span id="status-text">MCP Server Stopped</span> </div> <button id="start-server-btn" class="btn btn-success" onclick="startServer()">Start MCP Server</button> <button id="refresh-tools-btn" class="btn btn-primary" onclick="loadTools()" disabled>Refresh Tools</button> </div> <!-- Tools Section --> <div id="tools-section" style="display: none;"> <h2 class="section-title" id="tools-title">Available Tools</h2> <div id="tools-grid" class="tools-grid"> <!-- Tools will be loaded here --> </div> </div> <!-- Output Section --> <div class="output-section"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;"> <h2 class="section-title" style="margin-bottom: 0;">Output</h2> <button class="btn btn-primary" onclick="clearOutput()" style="background-color: #6c757d; font-size: 14px; padding: 8px 16px;"> 🗑️ Clear Output </button> </div> <div id="output" class="output-box"> Welcome to Logstash MCP Server Web UI! Click "Start MCP Server" to begin. </div> </div> </div> </div> <script> let serverRunning = false; let serverInitialized = false; // Check server status on page load window.onload = function() { checkServerStatus(); }; function addOutput(message, type = 'info') { const output = document.getElementById('output'); const timestamp = new Date().toLocaleTimeString(); const prefix = type === 'error' ? '❌' : type === 'success' ? '✅' : '📝'; output.textContent += `\n[${timestamp}] ${prefix} ${message}`; output.scrollTop = output.scrollHeight; } function clearOutput() { const output = document.getElementById('output'); output.textContent = 'Output cleared.\n\nReady for new operations...'; // Add a small visual feedback const clearBtn = event.target; const originalText = clearBtn.innerHTML; clearBtn.innerHTML = '✅ Cleared'; clearBtn.style.backgroundColor = '#28a745'; setTimeout(() => { clearBtn.innerHTML = originalText; clearBtn.style.backgroundColor = '#6c757d'; }, 1500); } async function checkServerStatus() { try { const response = await fetch('/server_status'); const data = await response.json(); updateServerStatus(data.running, data.initialized); } catch (error) { console.error('Error checking server status:', error); } } function updateServerStatus(running, initialized) { serverRunning = running; serverInitialized = initialized; const statusIndicator = document.querySelector('.status-indicator'); const statusText = document.getElementById('status-text'); const startBtn = document.getElementById('start-server-btn'); const refreshBtn = document.getElementById('refresh-tools-btn'); if (running && initialized) { statusIndicator.className = 'status-indicator status-running'; statusText.textContent = 'MCP Server Running & Initialized'; startBtn.disabled = true; refreshBtn.disabled = false; loadTools(); } else if (running) { statusIndicator.className = 'status-indicator status-running'; statusText.textContent = 'MCP Server Starting...'; startBtn.disabled = true; refreshBtn.disabled = true; } else { statusIndicator.className = 'status-indicator status-stopped'; statusText.textContent = 'MCP Server Stopped'; startBtn.disabled = false; refreshBtn.disabled = true; document.getElementById('tools-section').style.display = 'none'; } } async function startServer() { addOutput('Starting MCP server...'); try { const response = await fetch('/start_server', { method: 'POST', headers: {'Content-Type': 'application/json'} }); const data = await response.json(); if (data.success) { addOutput('MCP server started successfully!', 'success'); updateServerStatus(true, data.initialized); setTimeout(checkServerStatus, 1000); // Double-check status } else { addOutput('Failed to start MCP server', 'error'); } } catch (error) { addOutput(`Error starting server: ${error.message}`, 'error'); } } async function loadTools() { addOutput('Loading available tools...'); try { const response = await fetch('/list_tools'); const data = await response.json(); if (data.result && data.result.tools) { displayTools(data.result.tools); addOutput(`Loaded ${data.result.tools.length} tools successfully!`, 'success'); } else if (data.error) { addOutput(`Error loading tools: ${data.error.message || data.error}`, 'error'); } else { addOutput('No tools available', 'error'); } } catch (error) { addOutput(`Error loading tools: ${error.message}`, 'error'); } } function displayTools(tools) { const toolsGrid = document.getElementById('tools-grid'); const toolsSection = document.getElementById('tools-section'); const toolsTitle = document.getElementById('tools-title'); toolsGrid.innerHTML = ''; tools.forEach(tool => { const toolCard = createToolCard(tool); toolsGrid.appendChild(toolCard); }); toolsTitle.textContent = `Available Tools (${tools.length})`; toolsSection.style.display = 'block'; } function createToolCard(tool) { const card = document.createElement('div'); card.className = 'tool-card'; // Convert URLs in description to clickable links let description = tool.description; const urlRegex = /(https?:\/\/[^\s]+)/g; description = description.replace(urlRegex, '<a href="$1" target="_blank" style="color: #007bff; text-decoration: underline; word-break: break-all;">📚 Documentation</a>'); let inputsHtml = ''; const schema = tool.inputSchema; if (schema && schema.properties) { inputsHtml = '<div class="tool-inputs">'; for (const [propName, propSchema] of Object.entries(schema.properties)) { if (propSchema.type === 'boolean') { inputsHtml += ` <label> <input type="checkbox" id="${tool.name}_${propName}" ${propSchema.default ? 'checked' : ''}> ${propName} (${propSchema.description || 'Boolean parameter'}) </label> `; } else if (propSchema.type === 'integer') { inputsHtml += ` <input type="number" id="${tool.name}_${propName}" placeholder="${propName}" value="${propSchema.default || ''}" title="${propSchema.description || 'Integer parameter'}"> `; } else { inputsHtml += ` <input type="text" id="${tool.name}_${propName}" placeholder="${propName}" title="${propSchema.description || 'String parameter'}"> `; } } inputsHtml += '</div>'; } card.innerHTML = ` <h3>${tool.name}</h3> <p>${description}</p> ${inputsHtml} <button class="btn btn-primary" onclick="callTool('${tool.name}')">Execute</button> `; return card; } async function callTool(toolName) { addOutput(`Executing tool: ${toolName}...`); // Collect arguments from inputs const arguments = {}; const inputs = document.querySelectorAll(`[id^="${toolName}_"]`); inputs.forEach(input => { const paramName = input.id.replace(`${toolName}_`, ''); if (input.type === 'checkbox') { arguments[paramName] = input.checked; } else if (input.type === 'number') { if (input.value) arguments[paramName] = parseInt(input.value); } else if (input.value) { arguments[paramName] = input.value; } }); try { const response = await fetch('/call_tool', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ name: toolName, arguments: arguments }) }); const data = await response.json(); if (data.result && data.result.content) { const content = data.result.content[0].text; addOutput(`✅ ${toolName} result:\n${content}`, 'success'); } else if (data.error) { addOutput(`❌ ${toolName} error: ${data.error.message || JSON.stringify(data.error)}`, 'error'); } else { addOutput(`❓ ${toolName} - unexpected response: ${JSON.stringify(data)}`, 'error'); } } catch (error) { addOutput(`❌ Error calling ${toolName}: ${error.message}`, 'error'); } } </script> </body> </html>

Latest Blog Posts

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/mashhurs/logstash-mcp-server'

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