mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-14 04:13:39 +00:00
feat: mcp测试版
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
server:
|
||||
port: 9999
|
||||
spring:
|
||||
ai:
|
||||
openai:
|
||||
api-key: sk-xXe1WMPjhlVb1aiI1b4c6c8934D8463f9e4b67Ed8718B772
|
||||
base-url: https://api.pandarobot.chat/
|
||||
mcp:
|
||||
client:
|
||||
enabled: true
|
||||
name: call-mcp-server
|
||||
sse:
|
||||
connections:
|
||||
server1:
|
||||
url: http://127.0.0.1:6040
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"fileSystem": {
|
||||
"command": "D:\\software\\nodeJs\\npx.cmd",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-filesystem",
|
||||
"D:\\software\\sqlite"
|
||||
]
|
||||
},
|
||||
"sqlLite": {
|
||||
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
||||
"args": [
|
||||
"mcp-server-sqlite",
|
||||
"--db-path",
|
||||
"D:\\work-space-study\\spring-ai-mcp-demo\\mcp-client\\src\\main\\resources\\test.db"
|
||||
]
|
||||
},
|
||||
"fetch": {
|
||||
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
||||
"args": [
|
||||
"mcp-server-fetch"
|
||||
]
|
||||
},
|
||||
"baidu-map": {
|
||||
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
||||
"args": [
|
||||
"run",
|
||||
"--with",
|
||||
"mcp[cli]",
|
||||
"mcp",
|
||||
"run",
|
||||
"D:\\work-space-python\\python-baidu-map\\baidu_map_mcp_server\\map.py"
|
||||
],
|
||||
"env": {
|
||||
"BAIDU_MAPS_API_KEY": "{百度地图API-KEY}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"fileSystem": {
|
||||
"command": "D:\\software\\nodeJs\\npx.cmd",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-filesystem",
|
||||
"D:\\software\\sqlite"
|
||||
]
|
||||
},
|
||||
"sqlLite": {
|
||||
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
||||
"args": [
|
||||
"mcp-server-sqlite",
|
||||
"--db-path",
|
||||
"D:\\work-space-study\\spring-ai-mcp-demo\\mcp-client\\src\\main\\resources\\test.db"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AI 对话助手</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
<body class="bg-gray-100 min-h-screen">
|
||||
<div class="container mx-auto p-4 max-w-3xl">
|
||||
<!-- 标题 -->
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-800">AI 对话助手</h1>
|
||||
<p class="text-gray-600 mt-2">基于 Spring AI 的流式对话系统 By AhuCodingBeast</p>
|
||||
</div>
|
||||
|
||||
<!-- 聊天容器 -->
|
||||
<div id="chat-container" class="bg-white rounded-xl shadow-lg p-4 mb-4 h-[500px] overflow-y-auto space-y-4">
|
||||
<!-- 初始欢迎消息 -->
|
||||
<div class="ai-message flex items-start gap-3">
|
||||
<div class="bg-green-100 p-3 rounded-lg max-w-[85%]">
|
||||
<span class="text-gray-800">您好!我是AI助手,有什么可以帮您?</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 输入区域 -->
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="message-input"
|
||||
class="flex-1 border border-gray-300 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="输入您的问题...">
|
||||
<button id="send-button"
|
||||
class="bg-blue-500 text-white px-6 py-3 rounded-xl hover:bg-blue-600 transition-colors flex items-center">
|
||||
<span>发送</span>
|
||||
<svg id="loading-spinner" class="hidden w-4 h-4 ml-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const chatContainer = document.getElementById('chat-container');
|
||||
const messageInput = document.getElementById('message-input');
|
||||
const sendButton = document.getElementById('send-button');
|
||||
const loadingSpinner = document.getElementById('loading-spinner');
|
||||
|
||||
// 发送消息处理
|
||||
function handleSend() {
|
||||
const message = messageInput.value.trim();
|
||||
if (!message) return;
|
||||
|
||||
// 添加用户消息
|
||||
addMessage(message, 'user');
|
||||
messageInput.value = '';
|
||||
|
||||
// 构建API URL
|
||||
const apiUrl = new URL('http://localhost:9999/dashscope/chat-client/generate_stream');
|
||||
apiUrl.searchParams.append('id', '01');
|
||||
apiUrl.searchParams.append('prompt', message);
|
||||
|
||||
// 显示加载状态
|
||||
sendButton.disabled = true;
|
||||
loadingSpinner.classList.remove('hidden');
|
||||
|
||||
// 创建EventSource连接
|
||||
const eventSource = new EventSource(apiUrl);
|
||||
let aiMessageElement = null;
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log(data);
|
||||
const content = data.result?.output?.text || '';
|
||||
const finishReason = data.result?.metadata?.finishReason;
|
||||
|
||||
// 创建消息容器(如果不存在)
|
||||
if (!aiMessageElement) {
|
||||
aiMessageElement = addMessage('', 'ai');
|
||||
}
|
||||
|
||||
// 追加内容
|
||||
if (content) {
|
||||
aiMessageElement.querySelector('.message-content').textContent += content;
|
||||
autoScroll();
|
||||
}
|
||||
|
||||
// 处理结束
|
||||
if (finishReason === 'STOP') {
|
||||
eventSource.close();
|
||||
sendButton.disabled = false;
|
||||
loadingSpinner.classList.add('hidden');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析错误:', error);
|
||||
}
|
||||
};
|
||||
|
||||
eventSource.onerror = (error) => {
|
||||
console.error('连接错误:', error);
|
||||
eventSource.close();
|
||||
sendButton.disabled = false;
|
||||
loadingSpinner.classList.add('hidden');
|
||||
addMessage('对话连接异常,请重试', 'ai', true);
|
||||
};
|
||||
}
|
||||
|
||||
// 添加消息到容器
|
||||
function addMessage(content, type, isError = false) {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = `${type}-message flex items-start gap-3`;
|
||||
|
||||
const bubble = document.createElement('div');
|
||||
bubble.className = `p-3 rounded-lg max-w-[85%] ${
|
||||
type === 'user'
|
||||
? 'bg-blue-500 text-white ml-auto'
|
||||
: `bg-green-100 ${isError ? 'text-red-500' : 'text-gray-800'}`
|
||||
}`;
|
||||
|
||||
const contentSpan = document.createElement('span');
|
||||
contentSpan.className = 'message-content';
|
||||
contentSpan.textContent = content;
|
||||
|
||||
bubble.appendChild(contentSpan);
|
||||
messageDiv.appendChild(bubble);
|
||||
chatContainer.appendChild(messageDiv);
|
||||
|
||||
autoScroll();
|
||||
return bubble;
|
||||
}
|
||||
|
||||
// 自动滚动到底部
|
||||
function autoScroll() {
|
||||
chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||
}
|
||||
|
||||
// 事件监听
|
||||
sendButton.addEventListener('click', handleSend);
|
||||
messageInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handleSend();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user