Browse Source

feat: mcp-1.0.0

ageerle 1 month ago
parent
commit
d1006f50ad
26 changed files with 251 additions and 740 deletions
  1. 0 15
      ruoyi-admin/src/main/resources/application.yml
  2. 5 0
      ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/request/ChatRequest.java
  3. 0 83
      ruoyi-extend/call-mcp-server/pom.xml
  4. 0 13
      ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/CallMcpServerApplication.java
  5. 0 22
      ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/cofing/McpClientCfg.java
  6. 0 74
      ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/view/ChatController.java
  7. 0 21
      ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/view/IndexController.java
  8. 0 16
      ruoyi-extend/call-mcp-server/src/main/resources/application.yaml
  9. 0 40
      ruoyi-extend/call-mcp-server/src/main/resources/mcp-server-bak.json
  10. 0 20
      ruoyi-extend/call-mcp-server/src/main/resources/mcp-server.json
  11. 0 148
      ruoyi-extend/call-mcp-server/src/main/resources/templates/index.html
  12. 1 1
      ruoyi-extend/pom.xml
  13. 91 0
      ruoyi-extend/ruoyi-ai-mcp-webflux-server/pom.xml
  14. 34 0
      ruoyi-extend/ruoyi-ai-mcp-webflux-server/src/main/java/com/ivy/mcp/sse/client/ClientWebflux.java
  15. 42 0
      ruoyi-extend/ruoyi-ai-mcp-webflux-server/src/main/java/com/ivy/mcp/sse/server/McpWebfluxServerApplication.java
  16. 7 0
      ruoyi-extend/ruoyi-ai-mcp-webflux-server/src/main/resources/application.properties
  17. 0 64
      ruoyi-extend/ruoyi-mcp-server/pom.xml
  18. 0 16
      ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/McpServerApplication.java
  19. 0 100
      ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/config/McpServerConfig.java
  20. 0 20
      ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/service/McpCustomService.java
  21. 0 12
      ruoyi-extend/ruoyi-mcp-server/src/main/resources/application.yml
  22. 6 8
      ruoyi-modules-api/ruoyi-chat-api/pom.xml
  23. 1 1
      ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/config/ChatConfig.java
  24. 0 5
      ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/IChatService.java
  25. 0 7
      ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java
  26. 64 54
      ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/OpenAIServiceImpl.java

+ 0 - 15
ruoyi-admin/src/main/resources/application.yml

@@ -320,19 +320,4 @@ wechat:
         token:   ''
         token:   ''
         aesKey: ''
         aesKey: ''
 
 
-spring:
-  ai:
-    openai:
-      api-key: sk-xX
-      base-url: https://api.pandarobot.chat/
-    ollama:
-      base-url: http://localhost:11434
-    mcp:
-      client:
-        enabled: true
-        name: call-mcp-server
-        sse:
-          connections:
-            server1:
-              url: http://127.0.0.1:6040
 
 

+ 5 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/request/ChatRequest.java

@@ -41,6 +41,11 @@ public class ChatRequest {
      */
      */
     private Boolean search = Boolean.FALSE;
     private Boolean search = Boolean.FALSE;
 
 
+    /**
+     *  是否开启mcp
+     */
+    private Boolean isMcp = Boolean.FALSE;
+
     /**
     /**
      * 知识库id
      * 知识库id
      */
      */

+ 0 - 83
ruoyi-extend/call-mcp-server/pom.xml

@@ -1,83 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <groupId>org.ruoyi</groupId>
-        <artifactId>ruoyi-ai</artifactId>
-        <version>1.0.0</version>
-        <relativePath>../../pom.xml</relativePath>
-    </parent>
-    <artifactId>call-mcp-server</artifactId>
-    <name>Archetype - call-mcp-server</name>
-    <url>http://maven.apache.org</url>
-
-    <properties>
-        <java.version>17</java.version>
-    </properties>
-
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>org.springframework.ai</groupId>
-                <artifactId>spring-ai-bom</artifactId>
-                <version>1.0.0-M6</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-
-    <dependencies>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-test</artifactId>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-thymeleaf</artifactId>
-        </dependency>
-
-
-        <dependency>
-            <groupId>org.springframework.ai</groupId>
-            <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
-            <version>1.0.0-M6</version>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.ai</groupId>
-            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
-        </dependency>
-
-
-        <dependency>
-            <groupId>org.springframework.ai</groupId>
-            <artifactId>spring-ai-mcp</artifactId>
-        </dependency>
-
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-maven-plugin</artifactId>
-            </plugin>
-        </plugins>
-    </build>
-
-</project>

+ 0 - 13
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/CallMcpServerApplication.java

@@ -1,13 +0,0 @@
-package org.ruoyi.rocket.callmcpserver;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-@SpringBootApplication
-public class CallMcpServerApplication {
-
-    public static void main(String[] args) {
-        SpringApplication.run(CallMcpServerApplication.class, args);
-    }
-
-}

+ 0 - 22
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/cofing/McpClientCfg.java

@@ -1,22 +0,0 @@
-package org.ruoyi.rocket.callmcpserver.cofing;
-
-import io.modelcontextprotocol.client.McpClient;
-import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
-import org.springframework.context.annotation.Configuration;
-
-import java.time.Duration;
-
-
-/**
- * @author ageer
- */
-@Configuration
-public class McpClientCfg implements McpSyncClientCustomizer {
-
-
-    @Override
-    public void customize(String name, McpClient.SyncSpec spec) {
-        // do nothing
-        spec.requestTimeout(Duration.ofSeconds(30));
-    }
-}

+ 0 - 74
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/view/ChatController.java

@@ -1,74 +0,0 @@
-package org.ruoyi.rocket.callmcpserver.view;
-
-import jakarta.servlet.http.HttpServletResponse;
-import org.springframework.ai.chat.client.ChatClient;
-import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
-import org.springframework.ai.chat.memory.ChatMemory;
-import org.springframework.ai.chat.memory.InMemoryChatMemory;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import org.springframework.ai.tool.ToolCallbackProvider;
-import org.springframework.web.bind.annotation.*;
-import reactor.core.publisher.Flux;
-
-
-/**
- * @author jianzhang
- * 2025/03/18/下午8:00
- */
-@RestController
-@RequestMapping("/dashscope/chat-client")
-public class ChatController {
-
-    private final ChatClient chatClient;
-
-    private final ChatMemory chatMemory = new InMemoryChatMemory();
-
-    public ChatController(ChatClient.Builder chatClientBuilder,ToolCallbackProvider tools) {
-        this.chatClient = chatClientBuilder
-                .defaultTools(tools)
-                .defaultOptions(
-                        OpenAiChatOptions.builder().model("gpt-4o-mini").build())
-                .build();
-    }
-
-    @RequestMapping(value = "/generate_stream", method = RequestMethod.GET)
-    public Flux<ChatResponse> generateStream(HttpServletResponse response, @RequestParam("id") String id, @RequestParam("prompt") String prompt) {
-        response.setCharacterEncoding("UTF-8");
-        var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, id, 10);
-
-
-        Flux<ChatResponse> chatResponseFlux = this.chatClient.prompt(prompt)
-                .advisors(messageChatMemoryAdvisor)
-                .stream()
-                .chatResponse();
-
-        Flux<String> content = this.chatClient.prompt(prompt)
-                .advisors(messageChatMemoryAdvisor)
-                .stream()
-                .content();
-
-        content.subscribe(
-                content1 -> System.out.println("chatResponse"+content1)
-        );
-        return chatResponseFlux;
-
-
-    }
-
-
-    @GetMapping("/advisor/chat/{id}/{prompt}")
-    public Flux<String> advisorChat(
-            HttpServletResponse response,
-            @PathVariable String id,
-            @PathVariable String prompt) {
-
-        response.setCharacterEncoding("UTF-8");
-        var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, id, 10);
-        return this.chatClient.prompt(prompt)
-                .advisors(messageChatMemoryAdvisor).stream().content();
-    }
-
-
-
-}

+ 0 - 21
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/view/IndexController.java

@@ -1,21 +0,0 @@
-package org.ruoyi.rocket.callmcpserver.view;
-
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.GetMapping;
-
-/**
- * @author jianzhang
- * 2025/03/18/下午8:00
- */
-@Controller
-public class IndexController {
-
-    @GetMapping("/")
-    public String chat(Model model) {
-        //model.addAttribute("name", "User");
-        // 返回视图名称,对应 templates/index.html
-        return "index";
-    }
-
-}

+ 0 - 16
ruoyi-extend/call-mcp-server/src/main/resources/application.yaml

@@ -1,16 +0,0 @@
-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 - 40
ruoyi-extend/call-mcp-server/src/main/resources/mcp-server-bak.json

@@ -1,40 +0,0 @@
-{
-  "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 - 20
ruoyi-extend/call-mcp-server/src/main/resources/mcp-server.json

@@ -1,20 +0,0 @@
-{
-  "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 - 148
ruoyi-extend/call-mcp-server/src/main/resources/templates/index.html

@@ -1,148 +0,0 @@
-<!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>

+ 1 - 1
ruoyi-extend/pom.xml

@@ -14,7 +14,7 @@
     <packaging>pom</packaging>
     <packaging>pom</packaging>
 
 
     <modules>
     <modules>
-        <module>ruoyi-mcp-server</module>
+        <module>ruoyi-ai-mcp-webflux-server</module>
     </modules>
     </modules>
 
 
 </project>
 </project>

+ 91 - 0
ruoyi-extend/ruoyi-ai-mcp-webflux-server/pom.xml

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>3.4.4</version>
+        <relativePath /> <!-- lookup parent from repository -->
+    </parent>
+
+    <groupId>com.ivy.mcp</groupId>
+    <artifactId>ruoyi-ai-mcp-webflux-server</artifactId>
+    <version>1.0.0-M6</version>
+
+    <name>spring-ai-mcp-webflux-server</name>
+    <description>Spring AI MCP Server example and invoke by stdio</description>
+
+    <properties>
+        <java.version>17</java.version>
+        <spring-ai.version>1.0.0-M6</spring-ai.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.ai</groupId>
+                <artifactId>spring-ai-bom</artifactId>
+                <version>${spring-ai.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-mcp-server-webflux-spring-boot-starter</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <repositories>
+        <repository>
+            <name>Central Portal Snapshots</name>
+            <id>central-portal-snapshots</id>
+            <url>https://central.sonatype.com/repository/maven-snapshots/</url>
+            <releases>
+                <enabled>false</enabled>
+            </releases>
+            <snapshots>
+                <enabled>true</enabled>
+            </snapshots>
+        </repository>
+        <repository>
+            <id>spring-milestones</id>
+            <name>Spring Milestones</name>
+            <url>https://repo.spring.io/milestone</url>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+        <repository>
+            <id>spring-snapshots</id>
+            <name>Spring Snapshots</name>
+            <url>https://repo.spring.io/snapshot</url>
+            <releases>
+                <enabled>false</enabled>
+            </releases>
+        </repository>
+    </repositories>
+
+</project>

+ 34 - 0
ruoyi-extend/ruoyi-ai-mcp-webflux-server/src/main/java/com/ivy/mcp/sse/client/ClientWebflux.java

@@ -0,0 +1,34 @@
+package com.ivy.mcp.sse.client;
+
+
+import io.modelcontextprotocol.client.McpClient;
+import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
+import io.modelcontextprotocol.spec.McpSchema;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import java.util.Map;
+
+public class ClientWebflux {
+
+    public static void main(String[] args) {
+
+        var transport = new WebFluxSseClientTransport(WebClient.builder().baseUrl("http://localhost:8080"));
+        try (var client = McpClient.sync(transport).build()) {
+
+            client.initialize();
+//            client.ping();
+
+            McpSchema.ListToolsResult toolsList = client.listTools();
+            System.out.println("Available Tools = " + toolsList);
+
+            McpSchema.CallToolResult sumResult = client.callTool(new McpSchema.CallToolRequest("add",
+                    Map.of("a", 1, "b", 2)));
+            System.out.println("add a+ b =  " + sumResult.content().get(0));
+
+
+            McpSchema.CallToolResult currentTimResult = client.callTool(new McpSchema.CallToolRequest("getCurrentTime", Map.of()));
+            System.out.println("current time Response = " + currentTimResult);
+        }
+    }
+
+}

+ 42 - 0
ruoyi-extend/ruoyi-ai-mcp-webflux-server/src/main/java/com/ivy/mcp/sse/server/McpWebfluxServerApplication.java

@@ -0,0 +1,42 @@
+package com.ivy.mcp.sse.server;
+
+import org.springframework.ai.tool.ToolCallback;
+import org.springframework.ai.tool.ToolCallbacks;
+import org.springframework.ai.tool.annotation.Tool;
+import org.springframework.ai.tool.annotation.ToolParam;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@SpringBootApplication
+public class McpWebfluxServerApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(McpWebfluxServerApplication.class, args);
+    }
+
+
+    @Bean
+    public List<ToolCallback> tools(MyTools myTools) {
+        return List.of(ToolCallbacks.from(myTools));
+    }
+
+    @Service
+    public static class MyTools {
+
+        @Tool(description = "add two numbers")
+        public Integer add(@ToolParam(description = "first number") int a,
+                           @ToolParam(description = "second number") int b) {
+
+            return a + b;
+        }
+
+        @Tool(description = "get current time")
+        public LocalDateTime getCurrentTime() {
+            return LocalDateTime.now();
+        }
+    }
+}

+ 7 - 0
ruoyi-extend/ruoyi-ai-mcp-webflux-server/src/main/resources/application.properties

@@ -0,0 +1,7 @@
+spring.main.banner-mode=off
+logging.pattern.console=
+logging.file.name=mcp-server/spring-ai-mcp-webflux-server/target/target/mcp.mytools.log
+
+spring.ai.mcp.server.enabled=true
+spring.ai.mcp.server.name=webflux-server
+spring.ai.mcp.server.version=1.0.0

+ 0 - 64
ruoyi-extend/ruoyi-mcp-server/pom.xml

@@ -1,64 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <groupId>org.ruoyi</groupId>
-        <artifactId>ruoyi-ai</artifactId>
-        <version>${revision}</version>
-        <relativePath>../../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>ruoyi-mcp-server</artifactId>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>org.springframework.ai</groupId>
-                <artifactId>spring-ai-bom</artifactId>
-                <version>1.0.0-M6</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-test</artifactId>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.ai</groupId>
-            <artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.ai</groupId>
-            <artifactId>spring-ai-mcp</artifactId>
-        </dependency>
-
-<!--        <dependency>-->
-<!--            <groupId>org.ruoyi</groupId>-->
-<!--            <artifactId>ruoyi-system-api</artifactId>-->
-<!--            <exclusions>-->
-<!--                <exclusion>-->
-<!--                    <groupId>org.ruoyi</groupId>-->
-<!--                    <artifactId>ruoyi-common-translation</artifactId>-->
-<!--                </exclusion>-->
-<!--            </exclusions>-->
-<!--        </dependency>-->
-
-    </dependencies>
-</project>

+ 0 - 16
ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/McpServerApplication.java

@@ -1,16 +0,0 @@
-package org.ruoyi.mcp;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * @author ageer
- */
-@SpringBootApplication
-public class McpServerApplication {
-
-    public static void main(String[] args) {
-        SpringApplication.run(McpServerApplication.class, args);
-    }
-
-}

+ 0 - 100
ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/config/McpServerConfig.java

@@ -1,100 +0,0 @@
-package org.ruoyi.mcp.config;
-
-import org.ruoyi.mcp.service.McpCustomService;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.modelcontextprotocol.server.McpServerFeatures;
-import io.modelcontextprotocol.spec.McpSchema;
-import org.springframework.ai.tool.ToolCallbackProvider;
-import org.springframework.ai.tool.method.MethodToolCallbackProvider;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.EnableWebMvc;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-import java.util.List;
-import java.util.Map;
-import java.util.function.Consumer;
-
-
-/**
- * @author ageer
- */
-@Configuration
-@EnableWebMvc
-public class McpServerConfig implements WebMvcConfigurer {
-
-    @Bean
-    public ToolCallbackProvider openLibraryTools(McpCustomService mcpService) {
-        return MethodToolCallbackProvider.builder().toolObjects(mcpService).build();
-    }
-
-    @Bean
-    public List<McpServerFeatures.SyncResourceRegistration> resourceRegistrations() {
-
-        // Create a resource registration for system information
-        var systemInfoResource = new McpSchema.Resource(
-                "system://info",
-                "System Information",
-                "Provides basic system information including Java version, OS, etc.",
-                "application/json", null
-        );
-
-        var resourceRegistration = new McpServerFeatures.SyncResourceRegistration(systemInfoResource, (request) -> {
-            try {
-                var systemInfo = Map.of(
-                        "javaVersion", System.getProperty("java.version"),
-                        "osName", System.getProperty("os.name"),
-                        "osVersion", System.getProperty("os.version"),
-                        "osArch", System.getProperty("os.arch"),
-                        "processors", Runtime.getRuntime().availableProcessors(),
-                        "timestamp", System.currentTimeMillis());
-
-                String jsonContent = new ObjectMapper().writeValueAsString(systemInfo);
-
-                return new McpSchema.ReadResourceResult(
-                        List.of(new McpSchema.TextResourceContents(request.uri(), "application/json", jsonContent)));
-            }
-            catch (Exception e) {
-                throw new RuntimeException("Failed to generate system info", e);
-            }
-        });
-
-        return List.of(resourceRegistration);
-    }
-
-
-
-    @Bean
-    public List<McpServerFeatures.SyncPromptRegistration> promptRegistrations() {
-
-        var prompt = new McpSchema.Prompt("greeting", "A friendly greeting prompt",
-                List.of(new McpSchema.PromptArgument("name", "The name to greet", true)));
-
-        var promptRegistration = new McpServerFeatures.SyncPromptRegistration(prompt, getPromptRequest -> {
-
-            String nameArgument = (String) getPromptRequest.arguments().get("name");
-            if (nameArgument == null) {
-                nameArgument = "friend";
-            }
-
-            var userMessage = new McpSchema.PromptMessage(McpSchema.Role.USER,
-                    new McpSchema.TextContent("Hello " + nameArgument + "! How can I assist you today?"));
-
-            return new McpSchema.GetPromptResult("A personalized greeting message", List.of(userMessage));
-        });
-
-        return List.of(promptRegistration);
-    }
-
-
-    @Bean
-    public Consumer<List<McpSchema.Root>> rootsChangeConsumer() {
-        return roots -> {
-            System.out.println("rootsChange");
-        };
-    }
-
-
-
-
-}

+ 0 - 20
ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/service/McpCustomService.java

@@ -1,20 +0,0 @@
-package org.ruoyi.mcp.service;
-
-import org.springframework.ai.tool.annotation.Tool;
-import org.springframework.stereotype.Service;
-
-/**
- * @author ageer
- */
-@Service
-public class McpCustomService {
-
-	public record User(String userName, String userBalance) {
-	}
-
-	@Tool(description = "根据用户名称查询用户信息")
-	public User getUserBalance(String username) {
-		return new User("admin","99.99");
-	}
-
-}

+ 0 - 12
ruoyi-extend/ruoyi-mcp-server/src/main/resources/application.yml

@@ -1,12 +0,0 @@
-server:
-  port: 6040
-spring:
-  application:
-    name: mcp-server
-  ai:
-    mcp:
-      server:
-        name: webmvc-mcp-server
-        version: 1.0.0
-        type: SYNC
-        sse-message-endpoint: /mcp/messages

+ 6 - 8
ruoyi-modules-api/ruoyi-chat-api/pom.xml

@@ -55,19 +55,17 @@
 
 
         <dependency>
         <dependency>
             <groupId>org.springframework.ai</groupId>
             <groupId>org.springframework.ai</groupId>
-            <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
+            <artifactId>spring-ai-mcp-client-webflux-spring-boot-starter</artifactId>
         </dependency>
         </dependency>
 
 
-<!--        <dependency>-->
-<!--            <groupId>org.springframework.ai</groupId>-->
-<!--            <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>-->
-<!--        </dependency>-->
-
         <dependency>
         <dependency>
-            <groupId>org.springframework.ai</groupId>
-            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
+            <groupId>io.modelcontextprotocol.sdk</groupId>
+            <artifactId>mcp-spring-webflux</artifactId>
+            <version>0.8.0</version>
+            <scope>compile</scope>
         </dependency>
         </dependency>
 
 
+
     </dependencies>
     </dependencies>
 
 
 </project>
 </project>

+ 1 - 1
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/config/ChatConfig.java

@@ -36,7 +36,7 @@ public class ChatConfig {
         return openAiStreamClient;
         return openAiStreamClient;
     }
     }
 
 
-    public OpenAiStreamClient createOpenAiStreamClient(String apiHost, String apiKey) {
+    public static OpenAiStreamClient createOpenAiStreamClient(String apiHost, String apiKey) {
         HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger());
         HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger());
         httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
         httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
         OkHttpClient okHttpClient = new OkHttpClient.Builder()
         OkHttpClient okHttpClient = new OkHttpClient.Builder()

+ 0 - 5
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/IChatService.java

@@ -18,9 +18,4 @@ public interface IChatService {
     SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter);
     SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter);
 
 
 
 
-    /**
-     * 客户端发送消息到服务端
-     * @param chatRequest 请求对象
-     */
-    void mcpChat(ChatRequest chatRequest,SseEmitter emitter);
 }
 }

+ 0 - 7
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java

@@ -55,11 +55,4 @@ public interface ISseService {
      */
      */
     String wxCpChat(String prompt);
     String wxCpChat(String prompt);
 
 
-    /**
-     * 联网查询
-     *
-     * @param prompt 提示词
-     * @return 查询内容
-     */
-    String webSearch (String prompt);
 }
 }

+ 64 - 54
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/OpenAIServiceImpl.java

@@ -1,78 +1,88 @@
 package org.ruoyi.chat.service.chat.impl;
 package org.ruoyi.chat.service.chat.impl;
 
 
+import cn.hutool.json.JSONUtil;
+import io.modelcontextprotocol.client.McpClient;
+import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
+import io.modelcontextprotocol.spec.McpSchema;
+import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.ruoyi.chat.service.chat.IChatService;
 import org.ruoyi.chat.service.chat.IChatService;
-import org.ruoyi.common.chat.entity.chat.Message;
+import org.ruoyi.common.chat.entity.chat.ChatChoice;
+import org.ruoyi.common.chat.entity.chat.ChatCompletion;
+import org.ruoyi.common.chat.entity.chat.ChatCompletionResponse;
+import org.ruoyi.common.chat.entity.chat.Parameters;
+import org.ruoyi.common.chat.entity.chat.tool.ToolCallFunction;
+import org.ruoyi.common.chat.entity.chat.tool.Tools;
+import org.ruoyi.common.chat.entity.chat.tool.ToolsFunction;
+import org.ruoyi.common.chat.openai.OpenAiStreamClient;
 import org.ruoyi.common.chat.request.ChatRequest;
 import org.ruoyi.common.chat.request.ChatRequest;
-import org.springframework.ai.chat.client.ChatClient;
-import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
-import org.springframework.ai.chat.memory.ChatMemory;
-import org.springframework.ai.chat.memory.InMemoryChatMemory;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import org.springframework.ai.tool.ToolCallbackProvider;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
-import reactor.core.publisher.Flux;
-import reactor.core.scheduler.Schedulers;
 
 
 import java.io.IOException;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 
 
 
 
 @Service
 @Service
 @Slf4j
 @Slf4j
+@RequiredArgsConstructor
 public class OpenAIServiceImpl implements IChatService {
 public class OpenAIServiceImpl implements IChatService {
 
 
-   private final ChatClient chatClient;
+    private final OpenAiStreamClient openAiStreamClient;
 
 
-    private final ChatMemory chatMemory = new InMemoryChatMemory();
-
-
-    public OpenAIServiceImpl(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools) {
-        this.chatClient = chatClientBuilder
-                .defaultTools(tools)
-                .defaultOptions(
-                        OpenAiChatOptions.builder()
-                                .model("gpt-4o-mini")
-                                .temperature(0.4)
-                                .build())
-                .build();
-    }
 
 
     @Override
     @Override
     public SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter) {
     public SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter) {
-        return emitter;
-    }
+        WebFluxSseClientTransport webFluxSseClientTransport = new WebFluxSseClientTransport(WebClient.builder().baseUrl("http://localhost:8080"));
+        ChatCompletion completion = ChatCompletion
+                .builder()
+                .messages(chatRequest.getMessages())
+                .model(chatRequest.getModel())
+                .stream(false)
+                .build();
+        List<Tools> tools = new ArrayList<>();
+        try (var client = McpClient.sync(webFluxSseClientTransport).build()) {
+            client.initialize();
+            McpSchema.ListToolsResult toolsList = client.listTools();
 
 
-    @Override
-    public void mcpChat(ChatRequest chatRequest, SseEmitter emitter) {
-        List<Message> msgList = chatRequest.getMessages();
-        // 添加记忆
-        for (int i = 0; i < msgList.size(); i++) {
-            org.springframework.ai.chat.messages.Message springAiMessage = new UserMessage(msgList.get(i).getContent().toString());
-            chatMemory.add(String.valueOf(i), springAiMessage);
+            for (McpSchema.Tool mcpTool : toolsList.tools()) {
+
+                McpSchema.JsonSchema jsonSchema = mcpTool.inputSchema();
+
+                Parameters parameters = Parameters.builder()
+                        .type(jsonSchema.type())
+                        .properties(jsonSchema.properties())
+                        .required(jsonSchema.required()).build();
+
+                Tools tool = Tools.builder()
+                        .type(Tools.Type.FUNCTION.getName())
+                        .function(ToolsFunction.builder().name(mcpTool.name()).description(mcpTool.description()).parameters(parameters).build())
+                        .build();
+                tools.add(tool);
+            }
+
+            completion.setTools(tools);
+            ChatCompletionResponse chatCompletionResponse = openAiStreamClient.chatCompletion(completion);
+            String arguments = chatCompletionResponse.getChoices().get(0).getMessage().getToolCalls().get(0).getFunction().getArguments();
+            String name = chatCompletionResponse.getChoices().get(0).getMessage().getToolCalls().get(0).getFunction().getName();
+            Map<String, Object> map = JSONUtil.toBean(arguments, Map.class);
+            McpSchema.CallToolResult sumResult = client.callTool(new McpSchema.CallToolRequest(name, map));
+            System.out.println("add a+ b =  " + sumResult.content().get(0));
+
+
+            McpSchema.Content content = sumResult.content().get(0);
+
+            emitter.send(sumResult.content().get(0));
+
+        } catch (IOException e) {
+            emitter.completeWithError(e);
         }
         }
-        var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, chatRequest.getUserId().toString(), 10);
-
-        Flux<String> content = chatClient
-                .prompt(chatRequest.getPrompt())
-                .advisors(messageChatMemoryAdvisor)
-                .stream().content();
-
-        content.publishOn(Schedulers.boundedElastic())
-                .doOnNext(text -> {
-                    try {
-                        emitter.send(text);
-                    } catch (IOException e) {
-                        emitter.completeWithError(e);
-                    }
-                })
-                .doOnError(error -> {
-                    log.error("Error in SSE stream: ", error);
-                    emitter.completeWithError(error);
-                })
-                .doOnComplete(emitter::complete)
-                .subscribe();
+        emitter.complete();
+        return emitter;
+
     }
     }
+
 }
 }