Skip to main content
Glama
chat.html17.5 kB
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>MCP工具 - AI助手对话</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); height: 100vh; display: flex; flex-direction: column; } .header { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(20px); padding: 15px 20px; box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1); display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid rgba(255, 255, 255, 0.2); } .logo { display: flex; align-items: center; gap: 10px; font-size: 1.5em; font-weight: 700; color: #2c3e50; } .ai-provider-badge { background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; padding: 4px 12px; border-radius: 15px; font-size: 0.5em; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; box-shadow: 0 2px 8px rgba(40, 167, 69, 0.3); } .ai-provider-badge.not-configured { background: #dc3545; box-shadow: 0 2px 8px rgba(220, 53, 69, 0.3); } .nav-buttons { display: flex; gap: 10px; } .nav-btn { padding: 8px 16px; border: none; border-radius: 20px; background: #6c757d; color: white; text-decoration: none; font-size: 0.9em; transition: all 0.3s ease; } .nav-btn:hover { background: #5a6268; transform: translateY(-1px); } .nav-btn.primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } .chat-container { flex: 1; display: flex; flex-direction: column; max-width: 1000px; margin: 0 auto; width: 100%; padding: 20px; gap: 20px; } .messages-container { flex: 1; background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(20px); border-radius: 20px; padding: 20px; overflow-y: auto; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); border: 1px solid rgba(255, 255, 255, 0.2); } .message { margin-bottom: 20px; animation: slideIn 0.3s ease; } @keyframes slideIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .message.user { text-align: right; } .message.assistant { text-align: left; } .message-bubble { display: inline-block; padding: 15px 20px; border-radius: 20px; max-width: 70%; word-wrap: break-word; position: relative; } .message.user .message-bubble { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; } .message.assistant .message-bubble { background: #f8f9fa; color: #2c3e50; border: 1px solid #e9ecef; } .message-time { font-size: 0.75em; opacity: 0.6; margin-top: 5px; } .tool-call-info { background: rgba(40, 167, 69, 0.1); border: 1px solid #28a745; border-radius: 10px; padding: 10px; margin-top: 10px; font-size: 0.9em; } .tool-call-info .tool-name { font-weight: 600; color: #28a745; } .input-container { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(20px); border-radius: 20px; padding: 20px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); border: 1px solid rgba(255, 255, 255, 0.2); } .input-form { display: flex; gap: 15px; align-items: flex-end; } .input-group { flex: 1; } .input-textarea { width: 100%; min-height: 50px; max-height: 150px; padding: 15px; border: 2px solid #e9ecef; border-radius: 15px; font-size: 1em; font-family: inherit; resize: vertical; transition: all 0.3s ease; background: white; } .input-textarea:focus { outline: none; border-color: #667eea; box-shadow: 0 0 20px rgba(102, 126, 234, 0.3); } .send-btn { padding: 15px 25px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 15px; font-size: 1em; font-weight: 600; cursor: pointer; transition: all 0.3s ease; min-width: 80px; } .send-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4); } .send-btn:disabled { opacity: 0.6; cursor: not-allowed; } .welcome-message { text-align: center; color: #6c757d; font-style: italic; margin: 50px 0; } .welcome-message h2 { color: #2c3e50; margin-bottom: 15px; } .quick-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 15px; justify-content: center; } .quick-btn { padding: 8px 15px; background: #e9ecef; border: none; border-radius: 20px; font-size: 0.9em; cursor: pointer; transition: all 0.3s ease; color: #495057; } .quick-btn:hover { background: #667eea; color: white; transform: translateY(-1px); } .typing-indicator { display: none; margin-bottom: 20px; } .typing-indicator .message-bubble { background: #f8f9fa; border: 1px solid #e9ecef; padding: 15px 20px; } .typing-dots { display: flex; gap: 3px; } .typing-dot { width: 8px; height: 8px; border-radius: 50%; background: #6c757d; animation: typing 1.4s infinite; } .typing-dot:nth-child(2) { animation-delay: 0.2s; } .typing-dot:nth-child(3) { animation-delay: 0.4s; } @keyframes typing { 0%, 60%, 100% { transform: translateY(0); } 30% { transform: translateY(-10px); } } @media (max-width: 768px) { .chat-container { padding: 10px; } .message-bubble { max-width: 85%; } .quick-actions { justify-content: flex-start; } .input-form { flex-direction: column; gap: 10px; } .send-btn { align-self: flex-end; } } </style> </head> <body> <div class="header"> <div class="logo"> 🔧 MCP 工具测试 </div> <div class="nav-buttons"> <a href="/start" class="nav-btn">🔑 API Key</a> <a href="/mcp" class="nav-btn">📡 MCP协议</a> <button class="nav-btn primary" onclick="clearChat()">🗑️ 清空对话</button> </div> </div> <div class="chat-container"> <div class="messages-container" id="messagesContainer"> <div class="welcome-message"> <h2>👋 欢迎使用 MCP AI助手</h2> <p>我可以帮您查询天气、进行计算、创建文档等多种任务</p> <div class="quick-actions"> <button class="quick-btn" onclick="sendQuickMessage('查询北京天气')">🌤️ 查询天气</button> <button class="quick-btn" onclick="sendQuickMessage('现在几点了')">⏰ 当前时间</button> <button class="quick-btn" onclick="sendQuickMessage('计算 25*9/5+32')">🔢 数学计算</button> <button class="quick-btn" onclick="sendQuickMessage('创建文档,标题:会议记录,内容:讨论了项目进度')">📄 创建文档</button> </div> </div> <div class="typing-indicator" id="typingIndicator"> <div class="message-bubble"> <div class="typing-dots"> <div class="typing-dot"></div> <div class="typing-dot"></div> <div class="typing-dot"></div> </div> </div> </div> </div> <div class="input-container"> <form class="input-form" id="messageForm"> <div class="input-group"> <textarea class="input-textarea" id="messageInput" placeholder="输入您的消息...支持天气查询、数学计算、文档创建等功能" rows="1" ></textarea> </div> <button type="submit" class="send-btn" id="sendBtn"> 📤 发送 </button> </form> </div> </div> <script> let messageHistory = []; let isProcessing = false; const messagesContainer = document.getElementById('messagesContainer'); const messageForm = document.getElementById('messageForm'); const messageInput = document.getElementById('messageInput'); const sendBtn = document.getElementById('sendBtn'); const typingIndicator = document.getElementById('typingIndicator'); // 自动调整输入框高度 messageInput.addEventListener('input', function() { this.style.height = 'auto'; this.style.height = Math.min(this.scrollHeight, 150) + 'px'; }); // 处理表单提交 messageForm.addEventListener('submit', async (e) => { e.preventDefault(); const message = messageInput.value.trim(); if (message && !isProcessing) { await sendMessage(message); } }); // 快速消息 function sendQuickMessage(message) { messageInput.value = message; sendMessage(message); } // 发送消息 async function sendMessage(message) { if (isProcessing) return; isProcessing = true; sendBtn.disabled = true; sendBtn.textContent = '发送中...'; // 隐藏欢迎消息 const welcomeMessage = document.querySelector('.welcome-message'); if (welcomeMessage) { welcomeMessage.style.display = 'none'; } // 添加用户消息 addMessage('user', message); messageInput.value = ''; messageInput.style.height = 'auto'; // 显示输入指示器 showTypingIndicator(); try { const response = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ message: message, history: messageHistory }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || `HTTP ${response.status}`); } const data = await response.json(); // 隐藏输入指示器 hideTypingIndicator(); // 添加AI回复 addMessage('assistant', data.response, data.toolCalls); // 更新历史记录 messageHistory.push( { role: 'user', content: message }, { role: 'assistant', content: data.response, toolCalls: data.toolCalls } ); // 限制历史记录长度 if (messageHistory.length > 20) { messageHistory = messageHistory.slice(-20); } } catch (error) { console.error('发送消息失败:', error); hideTypingIndicator(); addMessage('assistant', `❌ 发送失败: ${error.message}\n\n这是一个简单的工具测试界面。您可以尝试:\n- 查询天气:北京天气\n- 数学计算:2+3*4\n- 获取时间:现在几点\n- 创建文档:创建文档,标题:测试`); } isProcessing = false; sendBtn.disabled = false; sendBtn.textContent = '📤 发送'; messageInput.focus(); } // 添加消息到界面 function addMessage(sender, content, toolCalls = []) { const messageDiv = document.createElement('div'); messageDiv.className = `message ${sender}`; const bubble = document.createElement('div'); bubble.className = 'message-bubble'; // 处理消息内容(支持换行) const formattedContent = content.replace(/\n/g, '<br>'); bubble.innerHTML = formattedContent; // 添加工具调用信息 if (toolCalls && toolCalls.length > 0) { toolCalls.forEach(toolCall => { const toolInfo = document.createElement('div'); toolInfo.className = 'tool-call-info'; toolInfo.innerHTML = ` <div class="tool-name">🔧 调用工具: ${toolCall.tool}</div> <div>参数: ${JSON.stringify(toolCall.arguments)}</div> `; bubble.appendChild(toolInfo); }); } const timeDiv = document.createElement('div'); timeDiv.className = 'message-time'; timeDiv.textContent = new Date().toLocaleTimeString('zh-CN'); messageDiv.appendChild(bubble); messageDiv.appendChild(timeDiv); // 插入到输入指示器前面 messagesContainer.insertBefore(messageDiv, typingIndicator); // 滚动到底部 messagesContainer.scrollTop = messagesContainer.scrollHeight; } // 显示输入指示器 function showTypingIndicator() { typingIndicator.style.display = 'block'; messagesContainer.scrollTop = messagesContainer.scrollHeight; } // 隐藏输入指示器 function hideTypingIndicator() { typingIndicator.style.display = 'none'; } // 清空对话 function clearChat() { if (confirm('确定要清空所有对话记录吗?')) { messageHistory = []; // 移除所有消息 const messages = messagesContainer.querySelectorAll('.message'); messages.forEach(msg => msg.remove()); // 显示欢迎消息 const welcomeMessage = document.querySelector('.welcome-message'); if (welcomeMessage) { welcomeMessage.style.display = 'block'; } } } // 键盘快捷键 messageInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); if (!isProcessing) { messageForm.dispatchEvent(new Event('submit')); } } }); // 页面加载完成后聚焦输入框 document.addEventListener('DOMContentLoaded', () => { messageInput.focus(); }); </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/starzzzzzzzzzzzzzz/mcp-tools'

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