mirror of
https://github.com/dromara/RuoYi-Vue-Plus.git
synced 2026-07-22 20:46:11 +00:00
update 优化 mcp代码案例
This commit is contained in:
+18
-7
@@ -5,12 +5,12 @@ 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.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -25,14 +25,13 @@ import java.util.Map;
|
||||
* <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 不会注册。
|
||||
* 当 `spring.ai.mcp.client.enabled=false` 时,接口会返回提示,不影响应用启动。
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/demo/mcp")
|
||||
@ConditionalOnBean(McpDemoClientService.class)
|
||||
public class McpDemoController {
|
||||
|
||||
private final McpDemoClientService mcpDemoClientService;
|
||||
@@ -41,8 +40,12 @@ public class McpDemoController {
|
||||
* 查询外部 MCP Server 工具列表。
|
||||
*/
|
||||
@GetMapping("/tools")
|
||||
public R<Map<String, java.util.List<String>>> tools() {
|
||||
return R.ok(mcpDemoClientService.listRemoteTools());
|
||||
public R<Map<String, List<String>>> tools() {
|
||||
try {
|
||||
return R.ok(mcpDemoClientService.listRemoteTools());
|
||||
} catch (IllegalStateException e) {
|
||||
return R.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,7 +54,11 @@ public class McpDemoController {
|
||||
@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)));
|
||||
try {
|
||||
return R.ok(mcpDemoClientService.receiveAndHandle(toolName, Map.of("id", id)));
|
||||
} catch (IllegalStateException e) {
|
||||
return R.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,6 +66,10 @@ public class McpDemoController {
|
||||
*/
|
||||
@GetMapping("/resource")
|
||||
public R<Map<String, McpResourceReadResult>> resource(@RequestParam(defaultValue = "demo://summary") String uri) {
|
||||
return R.ok(mcpDemoClientService.readRemoteResource(uri));
|
||||
try {
|
||||
return R.ok(mcpDemoClientService.readRemoteResource(uri));
|
||||
} catch (IllegalStateException e) {
|
||||
return R.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package org.dromara.demo.mcp;
|
||||
|
||||
import io.modelcontextprotocol.spec.McpSchema;
|
||||
import org.springframework.ai.mcp.annotation.McpElicitation;
|
||||
import org.springframework.ai.mcp.annotation.McpSampling;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MCP Client 能力回调演示。
|
||||
* <p>
|
||||
* `@McpSampling` 与 `@McpElicitation` 不是对外提供给 MCP Client 调用的工具,
|
||||
* 而是 MCP Server 在处理请求时,反向请求 MCP Client 进行模型采样或补充信息时的回调处理器。
|
||||
* <p>
|
||||
* 这里提供最小示例,主要用于展示注解写法,并让 Spring AI MCP 注解扫描器能扫描到
|
||||
* sampling/elicitation 方法,避免启动时输出 "No sampling methods found" 与
|
||||
* "No elicitation methods found" 提示。
|
||||
* <p>
|
||||
* Spring AI 2.0.0-M6 要求 `clients` 不能为空。这里使用 `ruoyi-demo` 作为示例客户端名称,
|
||||
* 实际使用时应与 `spring.ai.mcp.client.*.connections` 中的连接名保持一致。
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Component
|
||||
public class McpDemoClientHandlers {
|
||||
|
||||
/**
|
||||
* MCP sampling 回调示例。
|
||||
*
|
||||
* @param request 采样请求
|
||||
* @return 采样结果
|
||||
*/
|
||||
@McpSampling(clients = "ruoyi-demo")
|
||||
public McpSchema.CreateMessageResult sampling(McpSchema.CreateMessageRequest request) {
|
||||
String text = request.systemPrompt() == null ? "demo sampling response" : request.systemPrompt();
|
||||
return new McpSchema.CreateMessageResult(
|
||||
McpSchema.Role.ASSISTANT,
|
||||
new McpSchema.TextContent(text),
|
||||
"ruoyi-demo-sampling",
|
||||
McpSchema.CreateMessageResult.StopReason.END_TURN
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* MCP elicitation 回调示例。
|
||||
*
|
||||
* @param request 补充信息请求
|
||||
* @return 补充信息结果
|
||||
*/
|
||||
@McpElicitation(clients = "ruoyi-demo")
|
||||
public McpSchema.ElicitResult elicitation(McpSchema.ElicitRequest request) {
|
||||
return new McpSchema.ElicitResult(
|
||||
McpSchema.ElicitResult.Action.ACCEPT,
|
||||
Map.of("message", request.message())
|
||||
);
|
||||
}
|
||||
}
|
||||
+15
-6
@@ -4,11 +4,12 @@ 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.beans.factory.ObjectProvider;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* MCP Client 演示服务,从外部 MCP Server 接收数据后交给业务层处理。
|
||||
@@ -16,6 +17,7 @@ import java.util.Map;
|
||||
* 本服务会注入 Spring AI 自动创建的多个 `McpSyncClient`。当
|
||||
* `spring.ai.mcp.client.enabled=true` 且配置了多个 connection 时,
|
||||
* 这里可以统一遍历这些 MCP Server。
|
||||
* 未启用 MCP Client 时,本服务仍会注册,避免演示 Controller 影响应用启动。
|
||||
* <p>
|
||||
* 如果要用当前项目自己连自己测试,建议启动两个 `ruoyi-admin` 实例:
|
||||
* 一个作为 server,一个作为 client,client 指向 server 的地址。
|
||||
@@ -24,10 +26,9 @@ import java.util.Map;
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnBean(McpClientTemplate.class)
|
||||
public class McpDemoClientService {
|
||||
|
||||
private final McpClientTemplate mcpClientTemplate;
|
||||
private final ObjectProvider<McpClientTemplate> mcpClientTemplateProvider;
|
||||
|
||||
/**
|
||||
* 查询已接入的 MCP Server 与工具列表。
|
||||
@@ -35,7 +36,7 @@ public class McpDemoClientService {
|
||||
* @return Server 名称与工具名称列表
|
||||
*/
|
||||
public Map<String, List<String>> listRemoteTools() {
|
||||
return mcpClientTemplate.listTools();
|
||||
return execute(McpClientTemplate::listTools);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,7 +47,7 @@ public class McpDemoClientService {
|
||||
* @return 工具返回内容
|
||||
*/
|
||||
public Map<String, McpToolCallResult> callRemoteTool(String toolName, Map<String, Object> arguments) {
|
||||
return mcpClientTemplate.callTool(toolName, arguments);
|
||||
return execute(template -> template.callTool(toolName, arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +57,7 @@ public class McpDemoClientService {
|
||||
* @return 资源内容
|
||||
*/
|
||||
public Map<String, McpResourceReadResult> readRemoteResource(String uri) {
|
||||
return mcpClientTemplate.readResource(uri);
|
||||
return execute(template -> template.readResource(uri));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,6 +73,14 @@ public class McpDemoClientService {
|
||||
return new McpDemoHandleResult("MCP", true, callRemoteTool(toolName, arguments));
|
||||
}
|
||||
|
||||
private <T> T execute(Function<McpClientTemplate, T> action) {
|
||||
McpClientTemplate template = mcpClientTemplateProvider.getIfAvailable();
|
||||
if (template == null) {
|
||||
throw new IllegalStateException("MCP Client 未启用,请配置 spring.ai.mcp.client.enabled=true 并设置 connections");
|
||||
}
|
||||
return action.apply(template);
|
||||
}
|
||||
|
||||
/**
|
||||
* MCP 数据处理结果。
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user