Skip to main content
Glama

E-commerce Local MCP Server

chat-ui.html21.5 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>BitCommerz AI Assistant - Testing Interface</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: linear-gradient(135deg, #667eea 0%, #764ba2 100%); height: 100vh; display: flex; justify-content: center; align-items: center; } .chat-container { width: 90%; max-width: 800px; height: 80vh; background: white; border-radius: 10px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); display: flex; flex-direction: column; overflow: hidden; } .chat-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; display: flex; justify-content: space-between; align-items: center; } .chat-header h1 { font-size: 1.5rem; font-weight: 600; } .settings { display: flex; gap: 10px; align-items: center; } .settings label { font-size: 0.9rem; } .shop-selector { position: relative; display: inline-block; } .shop-search { padding: 5px 10px; border-radius: 5px; border: none; background: rgba(255,255,255,0.2); color: white; width: 200px; cursor: pointer; } .shop-search::placeholder { color: rgba(255,255,255,0.7); } .shop-dropdown { position: absolute; top: 100%; right: 0; background: white; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 300px; overflow-y: auto; display: none; z-index: 1000; width: 250px; margin-top: 5px; } .shop-dropdown.active { display: block; } .shop-item { padding: 10px; color: #333; cursor: pointer; border-bottom: 1px solid #eee; } .shop-item:hover { background: #f5f5f5; } .shop-item.selected { background: #e8f4ff; font-weight: bold; } .shop-item-info { font-size: 0.8rem; color: #666; } .chat-messages { flex: 1; overflow-y: auto; padding: 20px; background: #f7f8fa; } .message { margin-bottom: 15px; display: flex; animation: slideIn 0.3s ease-out; } @keyframes slideIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .message.user { justify-content: flex-end; } .message-content { max-width: 70%; padding: 12px 16px; border-radius: 18px; word-wrap: break-word; } .message.user .message-content { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-bottom-right-radius: 5px; } .message.assistant .message-content { background: white; color: #333; border-bottom-left-radius: 5px; box-shadow: 0 1px 2px rgba(0,0,0,0.1); } .message-label { font-size: 0.75rem; color: #666; margin-bottom: 4px; padding-left: 16px; } .message.user .message-label { text-align: right; padding-right: 16px; padding-left: 0; } .chat-input { padding: 20px; background: white; border-top: 1px solid #e0e0e0; } .input-wrapper { display: flex; gap: 10px; } .chat-input input { flex: 1; padding: 12px 16px; border: 2px solid #e0e0e0; border-radius: 25px; font-size: 1rem; outline: none; transition: border-color 0.3s; } .chat-input input:focus { border-color: #667eea; } .chat-input button { padding: 12px 24px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 25px; cursor: pointer; font-size: 1rem; transition: transform 0.2s; } .chat-input button:hover { transform: scale(1.05); } .chat-input button:disabled { opacity: 0.5; cursor: not-allowed; } .typing-indicator { display: none; padding: 20px; color: #666; } .typing-indicator.active { display: block; } .typing-indicator span { animation: blink 1.4s infinite; } .typing-indicator span:nth-child(2) { animation-delay: 0.2s; } .typing-indicator span:nth-child(3) { animation-delay: 0.4s; } @keyframes blink { 0%, 60%, 100% { opacity: 0.3; } 30% { opacity: 1; } } .error-message { background: #ff5252; color: white; padding: 10px; border-radius: 5px; margin: 10px 20px; display: none; } .error-message.show { display: block; } .metadata { font-size: 0.75rem; color: #999; margin-top: 4px; font-style: italic; } </style> </head> <body> <div class="chat-container"> <div class="chat-header"> <h1>BitCommerz AI Assistant</h1> <div class="settings"> <label>Shop:</label> <div class="shop-selector"> <input type="text" class="shop-search" id="shopSearch" placeholder="Search or select shop..." readonly /> <div class="shop-dropdown" id="shopDropdown"></div> </div> </div> </div> <div class="chat-messages" id="chatMessages"> <div class="message assistant"> <div> <div class="message-label">Assistant</div> <div class="message-content"> Hello! I'm your BitCommerz AI assistant. Ask me anything about your store data - products, sales, inventory, customers, or any other metrics! </div> </div> </div> </div> <div class="typing-indicator" id="typingIndicator"> Assistant is typing<span>.</span><span>.</span><span>.</span> </div> <div class="error-message" id="errorMessage"></div> <div class="chat-input"> <div class="input-wrapper"> <input type="text" id="messageInput" placeholder="Type your message here..." autocomplete="off" /> <button id="sendButton">Send</button> </div> </div> </div> <script> // Configuration const API_URL = 'http://localhost:8000/api/v1/chat/enhanced'; const SHOPS_API_URL = 'http://localhost:8000/api/v1/chat/shops'; // State let conversationId = null; let isProcessing = false; let allShops = []; let selectedShopId = 10; // Default shop // Elements const chatMessages = document.getElementById('chatMessages'); const messageInput = document.getElementById('messageInput'); const sendButton = document.getElementById('sendButton'); const typingIndicator = document.getElementById('typingIndicator'); const errorMessage = document.getElementById('errorMessage'); const shopSearch = document.getElementById('shopSearch'); const shopDropdown = document.getElementById('shopDropdown'); // Add message to chat function addMessage(content, isUser = false, metadata = null) { const messageDiv = document.createElement('div'); messageDiv.className = `message ${isUser ? 'user' : 'assistant'}`; let metadataHtml = ''; if (metadata && !isUser) { metadataHtml = ` <div class="metadata"> ${metadata.model_used ? `Model: ${metadata.model_used} | ` : ''} ${metadata.execution_time_ms ? `Time: ${metadata.execution_time_ms}ms | ` : ''} ${metadata.tools_called ? `Tools: ${metadata.tools_called.join(', ')}` : ''} </div> `; } messageDiv.innerHTML = ` <div> <div class="message-label">${isUser ? 'You' : 'Assistant'}</div> <div class="message-content"> ${content} ${metadataHtml} </div> </div> `; chatMessages.appendChild(messageDiv); chatMessages.scrollTop = chatMessages.scrollHeight; } // Show/hide typing indicator function setTyping(show) { typingIndicator.classList.toggle('active', show); if (show) { chatMessages.scrollTop = chatMessages.scrollHeight; } } // Show error function showError(message) { errorMessage.textContent = message; errorMessage.classList.add('show'); setTimeout(() => { errorMessage.classList.remove('show'); }, 5000); } // Load shops from API async function loadShops() { try { const response = await fetch(SHOPS_API_URL); const data = await response.json(); if (data.success) { allShops = data.shops; renderShopDropdown(allShops); // Set default shop const defaultShop = allShops.find(s => s.id === 10) || allShops[0]; if (defaultShop) { selectShop(defaultShop.id, defaultShop.name); } } } catch (error) { console.error('Failed to load shops:', error); // Use fallback shops allShops = [ {id: 10, name: 'Shop 10'}, {id: 1, name: 'Shop 1'}, {id: 2, name: 'Shop 2'} ]; renderShopDropdown(allShops); } } // Render shop dropdown function renderShopDropdown(shops) { shopDropdown.innerHTML = ''; shops.forEach(shop => { const item = document.createElement('div'); item.className = 'shop-item'; if (shop.id === selectedShopId) { item.classList.add('selected'); } item.innerHTML = ` <div>${shop.name} (ID: ${shop.id})</div> ${shop.status ? `<div class="shop-item-info">Status: ${shop.status}</div>` : ''} ${shop.order_count ? `<div class="shop-item-info">${shop.order_count} orders</div>` : ''} ${shop.product_count ? `<div class="shop-item-info">${shop.product_count} products</div>` : ''} `; item.onclick = () => { selectShop(shop.id, shop.name); closeShopDropdown(); }; shopDropdown.appendChild(item); }); } // Select a shop function selectShop(shopId, shopName) { selectedShopId = shopId; shopSearch.value = shopName; conversationId = null; // Reset conversation // Update selected state in dropdown document.querySelectorAll('.shop-item').forEach(item => { item.classList.remove('selected'); }); addMessage( `Switched to ${shopName}. Starting a new conversation.`, false ); } // Filter shops based on search function filterShops(searchTerm) { const filtered = allShops.filter(shop => shop.name.toLowerCase().includes(searchTerm.toLowerCase()) || shop.id.toString().includes(searchTerm) ); renderShopDropdown(filtered); } // Open shop dropdown function openShopDropdown() { shopDropdown.classList.add('active'); shopSearch.removeAttribute('readonly'); shopSearch.value = ''; shopSearch.focus(); renderShopDropdown(allShops); } // Close shop dropdown function closeShopDropdown() { shopDropdown.classList.remove('active'); shopSearch.setAttribute('readonly', true); const currentShop = allShops.find(s => s.id === selectedShopId); if (currentShop) { shopSearch.value = currentShop.name; } } // Send message async function sendMessage(message = null, isDisambiguation = false, originalQuery = null, selectedIntent = null) { message = message || messageInput.value.trim(); if (!message && !isDisambiguation) return; if (isProcessing) return; // Add user message if not disambiguation if (!isDisambiguation) { addMessage(message, true); messageInput.value = ''; } // Disable input isProcessing = true; sendButton.disabled = true; setTyping(true); try { let requestBody = { shop_id: selectedShopId, conversation_id: conversationId }; if (isDisambiguation) { requestBody.query = ''; requestBody.selected_intent = selectedIntent; requestBody.original_query = originalQuery; } else { requestBody.query = message; } const response = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(requestBody) }); const data = await response.json(); if (data.success) { conversationId = data.conversation_id; // Handle disambiguation if (data.needs_clarification) { handleDisambiguation(data); } else { addMessage(data.response, false, data.metadata); } } else { addMessage( data.response || 'Sorry, I encountered an error processing your request.', false ); if (data.error) { showError(`Error: ${data.error}`); } } } catch (error) { console.error('Error:', error); addMessage( 'Sorry, I couldn\'t connect to the server. Please make sure the API is running.', false ); showError(`Connection error: ${error.message}`); } finally { setTyping(false); isProcessing = false; sendButton.disabled = false; messageInput.focus(); } } // Handle disambiguation response function handleDisambiguation(data) { const disambiguationDiv = document.createElement('div'); disambiguationDiv.className = 'message assistant'; let optionsHtml = ''; data.options.forEach((option, index) => { optionsHtml += ` <button class="disambiguation-button" data-intent="${option.intent}" data-original="${data.metadata.original_query}" style="display: block; margin: 5px 0; padding: 10px 15px; background: white; border: 2px solid #667eea; border-radius: 10px; cursor: pointer; width: 100%; text-align: left; transition: all 0.2s;"> ${index + 1}. ${option.description} </button> `; }); disambiguationDiv.innerHTML = ` <div> <div class="message-label">Assistant</div> <div class="message-content"> <div>${data.question}</div> <div style="margin-top: 10px;"> ${optionsHtml} </div> </div> </div> `; chatMessages.appendChild(disambiguationDiv); chatMessages.scrollTop = chatMessages.scrollHeight; // Add event listeners to buttons const buttons = disambiguationDiv.querySelectorAll('.disambiguation-button'); buttons.forEach(button => { button.addEventListener('click', function() { const intent = this.getAttribute('data-intent'); const original = this.getAttribute('data-original'); // Add user's selection as a message addMessage(`Selected: ${this.textContent}`, true); // Disable all buttons buttons.forEach(b => b.disabled = true); // Send disambiguation response sendMessage(null, true, original, intent); }); button.addEventListener('mouseenter', function() { this.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'; this.style.color = 'white'; }); button.addEventListener('mouseleave', function() { this.style.background = 'white'; this.style.color = 'black'; }); }); } // Event listeners sendButton.addEventListener('click', sendMessage); messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); // Shop dropdown event listeners shopSearch.addEventListener('click', (e) => { e.stopPropagation(); if (shopDropdown.classList.contains('active')) { closeShopDropdown(); } else { openShopDropdown(); } }); shopSearch.addEventListener('input', (e) => { filterShops(e.target.value); }); // Close dropdown when clicking outside document.addEventListener('click', (e) => { if (!e.target.closest('.shop-selector')) { closeShopDropdown(); } }); // Load shops on page load loadShops(); // Focus on input when page loads messageInput.focus(); // Example queries const exampleQueries = [ "How many products do we have?", "What's our total revenue?", "Show me low stock products", "How much revenue in July?", "What are our top selling products?", "How many active customers?", "What's our average order value?" ]; // Add a help message with examples setTimeout(() => { addMessage( `Try asking me questions like:<br><br>` + exampleQueries.map(q => `• "${q}"`).join('<br>'), false ); }, 1000); </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/AnisurRahman06046/mcptestwithmodel'

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