办学质量监测教学评价系统
ageerle
2025-04-16 d1006f50ad802db5857aed54ffcc36043a7eef0c
feat: mcp-1.0.0
已修改8个文件
已添加4个文件
已删除14个文件
989 ■■■■ 文件已修改
ruoyi-admin/src/main/resources/application.yml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/request/ChatRequest.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/call-mcp-server/pom.xml 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/CallMcpServerApplication.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/cofing/McpClientCfg.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/view/ChatController.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/view/IndexController.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/call-mcp-server/src/main/resources/application.yaml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/call-mcp-server/src/main/resources/mcp-server-bak.json 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/call-mcp-server/src/main/resources/mcp-server.json 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/call-mcp-server/src/main/resources/templates/index.html 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-ai-mcp-webflux-server/pom.xml 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-ai-mcp-webflux-server/src/main/java/com/ivy/mcp/sse/client/ClientWebflux.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-ai-mcp-webflux-server/src/main/java/com/ivy/mcp/sse/server/McpWebfluxServerApplication.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-ai-mcp-webflux-server/src/main/resources/application.properties 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-mcp-server/pom.xml 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/McpServerApplication.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/config/McpServerConfig.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/service/McpCustomService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-mcp-server/src/main/resources/application.yml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules-api/ruoyi-chat-api/pom.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/config/ChatConfig.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/IChatService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/OpenAIServiceImpl.java 116 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml
@@ -320,19 +320,4 @@
        token:   ''
        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
ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/request/ChatRequest.java
@@ -42,6 +42,11 @@
    private Boolean search = Boolean.FALSE;
    /**
     *  æ˜¯å¦å¼€å¯mcp
     */
    private Boolean isMcp = Boolean.FALSE;
    /**
     * çŸ¥è¯†åº“id
     */
    private String kid;
ruoyi-extend/call-mcp-server/pom.xml
ÎļþÒÑɾ³ý
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/CallMcpServerApplication.java
ÎļþÒÑɾ³ý
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/cofing/McpClientCfg.java
ÎļþÒÑɾ³ý
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/view/ChatController.java
ÎļþÒÑɾ³ý
ruoyi-extend/call-mcp-server/src/main/java/org/ruoyi/rocket/callmcpserver/view/IndexController.java
ÎļþÒÑɾ³ý
ruoyi-extend/call-mcp-server/src/main/resources/application.yaml
ÎļþÒÑɾ³ý
ruoyi-extend/call-mcp-server/src/main/resources/mcp-server-bak.json
ÎļþÒÑɾ³ý
ruoyi-extend/call-mcp-server/src/main/resources/mcp-server.json
ÎļþÒÑɾ³ý
ruoyi-extend/call-mcp-server/src/main/resources/templates/index.html
ÎļþÒÑɾ³ý
ruoyi-extend/pom.xml
@@ -14,7 +14,7 @@
    <packaging>pom</packaging>
    <modules>
        <module>ruoyi-mcp-server</module>
        <module>ruoyi-ai-mcp-webflux-server</module>
    </modules>
</project>
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>
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);
        }
    }
}
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();
        }
    }
}
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
ruoyi-extend/ruoyi-mcp-server/pom.xml
ÎļþÒÑɾ³ý
ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/McpServerApplication.java
ÎļþÒÑɾ³ý
ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/config/McpServerConfig.java
ÎļþÒÑɾ³ý
ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/service/McpCustomService.java
ÎļþÒÑɾ³ý
ruoyi-extend/ruoyi-mcp-server/src/main/resources/application.yml
ÎļþÒÑɾ³ý
ruoyi-modules-api/ruoyi-chat-api/pom.xml
@@ -55,19 +55,17 @@
        <dependency>
            <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>-->
<!--            <groupId>org.springframework.ai</groupId>-->
<!--            <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>-->
<!--        </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>
    </dependencies>
</project>
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/config/ChatConfig.java
@@ -36,7 +36,7 @@
        return openAiStreamClient;
    }
    public OpenAiStreamClient createOpenAiStreamClient(String apiHost, String apiKey) {
    public static OpenAiStreamClient createOpenAiStreamClient(String apiHost, String apiKey) {
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger());
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/IChatService.java
@@ -18,9 +18,4 @@
    SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter);
    /**
     * å®¢æˆ·ç«¯å‘送消息到服务端
     * @param chatRequest è¯·æ±‚对象
     */
    void mcpChat(ChatRequest chatRequest,SseEmitter emitter);
}
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java
@@ -55,11 +55,4 @@
     */
    String wxCpChat(String prompt);
    /**
     * è”网查询
     *
     * @param prompt æç¤ºè¯
     * @return æŸ¥è¯¢å†…容
     */
    String webSearch (String prompt);
}
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;
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 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.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.web.reactive.function.client.WebClient;
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.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
@Slf4j
@RequiredArgsConstructor
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
    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);
        emitter.complete();
        return emitter;
        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();
    }
}