add 增加 ruoyi-common-mcp 模块 支持作为server提供mcp接口提供数据 与 接入多个client做数据采集
This commit is contained in:
@@ -98,6 +98,11 @@
|
||||
<artifactId>ruoyi-common-mqtt</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mcp</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package org.dromara.demo.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.mcp.core.McpResourceReadResult;
|
||||
import org.dromara.demo.mcp.McpDemoClientService;
|
||||
import org.dromara.demo.mcp.McpDemoClientService.McpDemoHandleResult;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MCP Client 演示案例。
|
||||
* <p>
|
||||
* 这些接口不是 MCP 协议接口,只是为了在后台系统中手动触发 MCP Client 调用。
|
||||
* 真实 MCP Server 入口由 Spring AI 挂载到 `/mcp`。
|
||||
* <p>
|
||||
* 调用示例:
|
||||
* <ul>
|
||||
* <li>`GET /demo/mcp/tools`:查看已连接 MCP Server 的工具列表</li>
|
||||
* <li>`GET /demo/mcp/receive?toolName=demo_get_data&id=1`:调用外部工具并模拟业务处理</li>
|
||||
* <li>`GET /demo/mcp/resource?uri=demo://summary`:读取外部 MCP 资源</li>
|
||||
* </ul>
|
||||
* 当 `spring.ai.mcp.client.enabled=false` 时,本 Controller 不会注册。
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/demo/mcp")
|
||||
@ConditionalOnBean(McpDemoClientService.class)
|
||||
public class McpDemoController {
|
||||
|
||||
private final McpDemoClientService mcpDemoClientService;
|
||||
|
||||
/**
|
||||
* 查询外部 MCP Server 工具列表。
|
||||
*/
|
||||
@GetMapping("/tools")
|
||||
public R<Map<String, java.util.List<String>>> tools() {
|
||||
return R.ok(mcpDemoClientService.listRemoteTools());
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用外部 MCP 工具并模拟业务处理。
|
||||
*/
|
||||
@GetMapping("/receive")
|
||||
public R<McpDemoHandleResult> receive(@RequestParam(defaultValue = "demo_get_data") String toolName,
|
||||
@RequestParam(defaultValue = "1") String id) {
|
||||
return R.ok(mcpDemoClientService.receiveAndHandle(toolName, Map.of("id", id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取外部 MCP 资源。
|
||||
*/
|
||||
@GetMapping("/resource")
|
||||
public R<Map<String, McpResourceReadResult>> resource(@RequestParam(defaultValue = "demo://summary") String uri) {
|
||||
return R.ok(mcpDemoClientService.readRemoteResource(uri));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.dromara.demo.mcp;
|
||||
|
||||
import org.dromara.common.mcp.core.McpClientTemplate;
|
||||
import org.dromara.common.mcp.core.McpResourceReadResult;
|
||||
import org.dromara.common.mcp.core.McpToolCallResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MCP Client 演示服务,从外部 MCP Server 接收数据后交给业务层处理。
|
||||
* <p>
|
||||
* 本服务会注入 Spring AI 自动创建的多个 `McpSyncClient`。当
|
||||
* `spring.ai.mcp.client.enabled=true` 且配置了多个 connection 时,
|
||||
* 这里可以统一遍历这些 MCP Server。
|
||||
* <p>
|
||||
* 如果要用当前项目自己连自己测试,建议启动两个 `ruoyi-admin` 实例:
|
||||
* 一个作为 server,一个作为 client,client 指向 server 的地址。
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnBean(McpClientTemplate.class)
|
||||
public class McpDemoClientService {
|
||||
|
||||
private final McpClientTemplate mcpClientTemplate;
|
||||
|
||||
/**
|
||||
* 查询已接入的 MCP Server 与工具列表。
|
||||
*
|
||||
* @return Server 名称与工具名称列表
|
||||
*/
|
||||
public Map<String, List<String>> listRemoteTools() {
|
||||
return mcpClientTemplate.listTools();
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用外部 MCP 工具接收数据。
|
||||
*
|
||||
* @param toolName 工具名称
|
||||
* @param arguments 工具参数
|
||||
* @return 工具返回内容
|
||||
*/
|
||||
public Map<String, McpToolCallResult> callRemoteTool(String toolName, Map<String, Object> arguments) {
|
||||
return mcpClientTemplate.callTool(toolName, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取外部 MCP 资源。
|
||||
*
|
||||
* @param uri 资源地址
|
||||
* @return 资源内容
|
||||
*/
|
||||
public Map<String, McpResourceReadResult> readRemoteResource(String uri) {
|
||||
return mcpClientTemplate.readResource(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟业务处理入口。真实业务可在这里做校验、转换、去重、入库和审计。
|
||||
* <p>
|
||||
* MCP 返回数据不建议直接入库,应先转换成业务 BO/DTO,再进入业务 Service。
|
||||
*
|
||||
* @param toolName 工具名称
|
||||
* @param arguments 工具参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
public McpDemoHandleResult receiveAndHandle(String toolName, Map<String, Object> arguments) {
|
||||
return new McpDemoHandleResult("MCP", true, callRemoteTool(toolName, arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
* MCP 数据处理结果。
|
||||
*
|
||||
* @param sourceType 数据来源类型
|
||||
* @param handled 是否已处理
|
||||
* @param data MCP 原始返回数据
|
||||
*/
|
||||
public record McpDemoHandleResult(
|
||||
String sourceType,
|
||||
boolean handled,
|
||||
Map<String, McpToolCallResult> data
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.dromara.demo.mcp;
|
||||
|
||||
import org.springframework.ai.mcp.annotation.McpResource;
|
||||
import org.springframework.ai.mcp.annotation.McpTool;
|
||||
import org.springframework.ai.mcp.annotation.McpToolParam;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* MCP Server 演示工具,对外提供业务数据。
|
||||
* <p>
|
||||
* 启动 `ruoyi-admin` 后,本类中的 `@McpTool` 与 `@McpResource` 会被 Spring AI
|
||||
* MCP 注解扫描器注册到当前应用的 MCP Server,默认通过同端口 `/mcp` 暴露。
|
||||
* <p>
|
||||
* 可被外部 MCP Client 调用的示例能力:
|
||||
* <ul>
|
||||
* <li>`demo_get_data`:根据 id 查询一条演示数据</li>
|
||||
* <li>`demo_list_data`:按 limit 查询演示数据列表</li>
|
||||
* <li>`demo://summary`:读取演示模块摘要资源</li>
|
||||
* </ul>
|
||||
* 真实业务中不要在 MCP 工具里直接访问 Mapper,应继续调用业务 Service,
|
||||
* 并保留权限、租户、脱敏、审计和幂等规则。
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Component
|
||||
public class McpDemoServerTool {
|
||||
|
||||
/**
|
||||
* 查询一条演示数据。
|
||||
*
|
||||
* @param id 业务主键
|
||||
* @return 演示数据
|
||||
*/
|
||||
@McpTool(name = "demo_get_data", description = "根据业务主键查询演示数据")
|
||||
public McpDemoData getData(@McpToolParam(description = "业务主键") String id) {
|
||||
return new McpDemoData(id, "演示数据-" + id, "ruoyi-demo", 100, LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询演示数据列表。
|
||||
*
|
||||
* @param limit 返回条数
|
||||
* @return 演示数据列表
|
||||
*/
|
||||
@McpTool(name = "demo_list_data", description = "查询演示数据列表")
|
||||
public List<McpDemoData> listData(@McpToolParam(description = "返回条数") Integer limit) {
|
||||
int size = limit == null || limit < 1 ? 3 : Math.min(limit, 20);
|
||||
return java.util.stream.IntStream.rangeClosed(1, size)
|
||||
.mapToObj(index -> new McpDemoData(String.valueOf(index), "演示数据-" + index, "ruoyi-demo", index * 10, LocalDateTime.now()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取演示资源。
|
||||
*
|
||||
* @return 演示资源内容
|
||||
*/
|
||||
@McpResource(
|
||||
name = "demo_summary",
|
||||
uri = "demo://summary",
|
||||
description = "演示模块摘要资源",
|
||||
mimeType = "text/plain"
|
||||
)
|
||||
public String summary() {
|
||||
return "ruoyi-demo MCP resource summary";
|
||||
}
|
||||
|
||||
/**
|
||||
* MCP 演示数据。
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public record McpDemoData(
|
||||
String id,
|
||||
String name,
|
||||
String source,
|
||||
Integer amount,
|
||||
LocalDateTime syncTime
|
||||
) {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user