diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java index 163fe7eb9..e47f50a01 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java @@ -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; *
+ * `@McpSampling` 与 `@McpElicitation` 不是对外提供给 MCP Client 调用的工具, + * 而是 MCP Server 在处理请求时,反向请求 MCP Client 进行模型采样或补充信息时的回调处理器。 + *
+ * 这里提供最小示例,主要用于展示注解写法,并让 Spring AI MCP 注解扫描器能扫描到 + * sampling/elicitation 方法,避免启动时输出 "No sampling methods found" 与 + * "No elicitation methods found" 提示。 + *
+ * 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()) + ); + } +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java index da4e3701b..f8983ab7c 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java @@ -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 影响应用启动。 *
* 如果要用当前项目自己连自己测试,建议启动两个 `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 mcpClientTemplateProvider; /** * 查询已接入的 MCP Server 与工具列表。 @@ -35,7 +36,7 @@ public class McpDemoClientService { * @return Server 名称与工具名称列表 */ public Map> listRemoteTools() { - return mcpClientTemplate.listTools(); + return execute(McpClientTemplate::listTools); } /** @@ -46,7 +47,7 @@ public class McpDemoClientService { * @return 工具返回内容 */ public Map callRemoteTool(String toolName, Map arguments) { - return mcpClientTemplate.callTool(toolName, arguments); + return execute(template -> template.callTool(toolName, arguments)); } /** @@ -56,7 +57,7 @@ public class McpDemoClientService { * @return 资源内容 */ public Map 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 execute(Function 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 数据处理结果。 *