<!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>