Skip to main content
Glama

MCP Art Supply Store

by wspotter
script.js10.4 kB
// API base URL const API_BASE = 'http://localhost:3000/api'; // State let allTools = []; let currentCategory = 'all'; let searchQuery = ''; // DOM Elements const statusDot = document.getElementById('statusDot'); const statusText = document.getElementById('statusText'); const uptimeEl = document.getElementById('uptime'); const toolCallsEl = document.getElementById('toolCalls'); const toolsGrid = document.getElementById('toolsGrid'); const searchInput = document.getElementById('searchTools'); const toolCount = document.getElementById('toolCount'); const currentCategoryEl = document.getElementById('currentCategory'); const testPanel = document.getElementById('testPanel'); const closePanel = document.getElementById('closePanel'); const testToolForm = document.getElementById('testToolForm'); const testResult = document.getElementById('testResult'); const activityLog = document.getElementById('activityLog'); const clearLogBtn = document.getElementById('clearLog'); // Category navigation const navItems = document.querySelectorAll('.nav-item'); // Initialize document.addEventListener('DOMContentLoaded', () => { fetchStatus(); fetchTools(); setupEventListeners(); // Poll for updates every 2 seconds setInterval(fetchStatus, 2000); }); // Setup event listeners function setupEventListeners() { // Category navigation navItems.forEach(item => { item.addEventListener('click', () => { const category = item.dataset.category; selectCategory(category); }); }); // Search searchInput.addEventListener('input', (e) => { searchQuery = e.target.value.toLowerCase(); filterAndDisplayTools(); }); // Test panel closePanel.addEventListener('click', () => { testPanel.classList.remove('open'); }); // Test form testToolForm.addEventListener('submit', async (e) => { e.preventDefault(); await executeTest(); }); // Clear log clearLogBtn.addEventListener('click', () => { activityLog.innerHTML = '<div class="log-entry">Log cleared</div>'; }); } // Fetch server status async function fetchStatus() { try { const response = await fetch(`${API_BASE}/status`); const data = await response.json(); statusText.textContent = data.status.charAt(0).toUpperCase() + data.status.slice(1); statusDot.style.background = 'var(--success-color)'; // Format uptime const uptimeSeconds = Math.floor(data.uptime / 1000); const hours = Math.floor(uptimeSeconds / 3600); const minutes = Math.floor((uptimeSeconds % 3600) / 60); uptimeEl.textContent = `${hours}h ${minutes}m`; toolCallsEl.textContent = data.toolCalls; } catch (error) { statusText.textContent = 'Offline'; statusDot.style.background = 'var(--danger-color)'; console.error('Failed to fetch status:', error); } } // Fetch available tools async function fetchTools() { try { const response = await fetch(`${API_BASE}/tools`); const data = await response.json(); allTools = data.tools; // Update category counts if (data.categories) { Object.entries(data.categories).forEach(([category, count]) => { const countEl = document.getElementById(`count-${getCategoryId(category)}`); if (countEl) countEl.textContent = count; }); // Update "All" count const totalCount = Object.values(data.categories).reduce((sum, count) => sum + count, 0); const allCountEl = document.getElementById('count-all'); if (allCountEl) allCountEl.textContent = totalCount; } filterAndDisplayTools(); logActivity('Loaded ' + allTools.length + ' tools'); } catch (error) { console.error('Failed to fetch tools:', error); toolsGrid.innerHTML = '<div class="empty-message"><div class="empty-message-icon">⚠️</div><div>Failed to load tools</div></div>'; } } // Get category ID for element lookup function getCategoryId(category) { const idMap = { 'Inventory Management': 'inventory', 'Customer Management': 'customers', 'Sales & Analytics': 'sales', 'Supplier Management': 'suppliers', 'Operations': 'operations', 'Pricing & Promotions': 'pricing', 'Reporting': 'reporting', 'Social Media Management': 'social' }; return idMap[category] || category.toLowerCase().replace(/ /g, '-'); } // Select category function selectCategory(category) { currentCategory = category; // Update nav active state navItems.forEach(item => { if (item.dataset.category === category) { item.classList.add('active'); } else { item.classList.remove('active'); } }); // Update category label const categoryLabel = category === 'all' ? 'All Categories' : category; currentCategoryEl.textContent = categoryLabel; filterAndDisplayTools(); } // Filter and display tools function filterAndDisplayTools() { let filtered = allTools; // Filter by category if (currentCategory !== 'all') { filtered = filtered.filter(tool => tool.category === currentCategory); } // Filter by search if (searchQuery) { filtered = filtered.filter(tool => tool.name.toLowerCase().includes(searchQuery) || tool.description.toLowerCase().includes(searchQuery) || tool.category.toLowerCase().includes(searchQuery) ); } // Update count toolCount.textContent = `${filtered.length} tool${filtered.length !== 1 ? 's' : ''}`; // Display displayTools(filtered); } // Display tools in grid function displayTools(tools) { if (tools.length === 0) { toolsGrid.innerHTML = ` <div class="empty-message"> <div class="empty-message-icon">🔍</div> <div>No tools found</div> </div> `; return; } toolsGrid.innerHTML = tools.map(tool => ` <div class="tool-card" data-tool-name="${tool.name}"> <div class="tool-card-header"> <div class="tool-card-name">${tool.name}</div> <div class="tool-card-badge">${getCategoryIcon(tool.category)}</div> </div> <div class="tool-card-description">${tool.description}</div> <div class="tool-card-footer"> <div class="tool-call-count"> <span>📞</span> <span>${tool.callCount || 0} calls</span> </div> <span style="color: var(--primary-color); font-weight: 600;">Test →</span> </div> </div> `).join(''); // Add click handlers document.querySelectorAll('.tool-card').forEach(card => { card.addEventListener('click', () => { const toolName = card.dataset.toolName; openTestPanel(toolName); }); }); } // Get category icon function getCategoryIcon(category) { const icons = { 'Inventory Management': '📦', 'Customer Management': '👥', 'Sales & Analytics': '📊', 'Supplier Management': '🏢', 'Operations': '⚙️', 'Pricing & Promotions': '💲', 'Reporting': '📈', 'Social Media Management': '📱' }; return icons[category] || '🔧'; } // Open test panel function openTestPanel(toolName) { const tool = allTools.find(t => t.name === toolName); if (!tool) return; // Update panel info document.getElementById('testToolName').textContent = tool.name; document.getElementById('testToolDesc').textContent = tool.description; // Clear previous result testResult.classList.remove('visible', 'success', 'error'); testResult.innerHTML = ''; // Clear input document.getElementById('toolInput').value = ''; // Store current tool testToolForm.dataset.toolName = toolName; // Open panel testPanel.classList.add('open'); logActivity(`Opened test panel for ${toolName}`); } // Execute test async function executeTest() { const toolName = testToolForm.dataset.toolName; const inputText = document.getElementById('toolInput').value; let input = {}; if (inputText.trim()) { try { input = JSON.parse(inputText); } catch (error) { showResult(false, 'Invalid JSON: ' + error.message); return; } } try { const response = await fetch(`${API_BASE}/test-tool`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ toolName, input }) }); const data = await response.json(); if (data.success) { showResult(true, JSON.stringify(data, null, 2)); logActivity(`✅ ${toolName} executed successfully`, 'success'); } else { showResult(false, data.error || 'Tool execution failed'); logActivity(`❌ ${toolName} failed: ${data.error}`, 'error'); } // Refresh status to update call count fetchStatus(); } catch (error) { showResult(false, 'Request failed: ' + error.message); logActivity(`❌ ${toolName} error: ${error.message}`, 'error'); } } // Show test result function showResult(success, message) { testResult.classList.add('visible'); testResult.classList.toggle('success', success); testResult.classList.toggle('error', !success); testResult.innerHTML = `<pre>${message}</pre>`; } // Log activity function logActivity(message, type = '') { const timestamp = new Date().toLocaleTimeString(); const entry = document.createElement('div'); entry.className = 'log-entry' + (type ? ' ' + type : ''); entry.textContent = `[${timestamp}] ${message}`; activityLog.insertBefore(entry, activityLog.firstChild); // Keep only last 50 entries while (activityLog.children.length > 50) { activityLog.removeChild(activityLog.lastChild); } }

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/wspotter/mcpart'

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