mcpSyncClients) {
+ return new McpClientTemplate(mcpSyncClients);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpClientTemplate.java b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpClientTemplate.java
new file mode 100644
index 000000000..962665cdd
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpClientTemplate.java
@@ -0,0 +1,125 @@
+package org.dromara.common.mcp.core;
+
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.spec.McpSchema;
+import lombok.RequiredArgsConstructor;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * MCP Client 通用操作模板。
+ *
+ * Spring AI 已经负责 MCP Client 的创建、初始化与连接管理,本模板只做项目内常用调用封装,
+ * 避免业务模块直接遍历 `McpSyncClient` 或重复处理返回结构。
+ *
+ * @author Lion Li
+ */
+@RequiredArgsConstructor
+public class McpClientTemplate {
+
+ private final List mcpSyncClients;
+
+ /**
+ * 查询所有已连接 MCP Server 的工具列表。
+ *
+ * @return Server 名称与工具名称列表
+ */
+ public Map> listTools() {
+ Map> result = new LinkedHashMap<>();
+ for (McpSyncClient client : mcpSyncClients) {
+ List tools = client.listTools().tools().stream()
+ .map(McpSchema.Tool::name)
+ .toList();
+ result.put(getServerName(client), tools);
+ }
+ return result;
+ }
+
+ /**
+ * 调用所有 MCP Server 上的同名工具。
+ *
+ * @param toolName 工具名称
+ * @param arguments 工具参数
+ * @return 各 Server 的工具调用结果
+ */
+ public Map callTool(String toolName, Map arguments) {
+ Map result = new LinkedHashMap<>();
+ for (McpSyncClient client : mcpSyncClients) {
+ McpSchema.CallToolResult callResult = client.callTool(new McpSchema.CallToolRequest(toolName, arguments));
+ result.put(getServerName(client), McpToolCallResult.of(getServerName(client), callResult));
+ }
+ return result;
+ }
+
+ /**
+ * 调用指定 MCP Server 上的工具。
+ *
+ * @param serverName Server 名称
+ * @param toolName 工具名称
+ * @param arguments 工具参数
+ * @return 工具调用结果
+ */
+ public Optional callTool(String serverName, String toolName, Map arguments) {
+ return findClient(serverName)
+ .map(client -> McpToolCallResult.of(serverName, client.callTool(new McpSchema.CallToolRequest(toolName, arguments))));
+ }
+
+ /**
+ * 读取所有 MCP Server 上的同名资源。
+ *
+ * @param uri 资源地址
+ * @return 各 Server 的资源内容
+ */
+ public Map readResource(String uri) {
+ Map result = new LinkedHashMap<>();
+ for (McpSyncClient client : mcpSyncClients) {
+ McpSchema.ReadResourceResult readResult = client.readResource(new McpSchema.ReadResourceRequest(uri));
+ result.put(getServerName(client), McpResourceReadResult.of(getServerName(client), readResult));
+ }
+ return result;
+ }
+
+ /**
+ * 读取指定 MCP Server 上的资源。
+ *
+ * @param serverName Server 名称
+ * @param uri 资源地址
+ * @return 资源内容
+ */
+ public Optional readResource(String serverName, String uri) {
+ return findClient(serverName)
+ .map(client -> McpResourceReadResult.of(serverName, client.readResource(new McpSchema.ReadResourceRequest(uri))));
+ }
+
+ /**
+ * 按 Server 名称查找 MCP Client。
+ *
+ * @param serverName Server 名称
+ * @return MCP Client
+ */
+ public Optional findClient(String serverName) {
+ return mcpSyncClients.stream()
+ .filter(client -> serverName.equals(getServerName(client)))
+ .findFirst();
+ }
+
+ /**
+ * 查询已连接的 MCP Client。
+ *
+ * @return MCP Client 列表
+ */
+ public List getClients() {
+ return mcpSyncClients;
+ }
+
+ private String getServerName(McpSyncClient client) {
+ McpSchema.Implementation serverInfo = client.getServerInfo();
+ if (serverInfo == null || serverInfo.name() == null) {
+ return "unknown";
+ }
+ return serverInfo.name();
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpResourceReadResult.java b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpResourceReadResult.java
new file mode 100644
index 000000000..afdaab154
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpResourceReadResult.java
@@ -0,0 +1,20 @@
+package org.dromara.common.mcp.core;
+
+import io.modelcontextprotocol.spec.McpSchema;
+
+import java.util.List;
+
+/**
+ * MCP 资源读取结果。
+ *
+ * @author Lion Li
+ */
+public record McpResourceReadResult(
+ String serverName,
+ List contents
+) {
+
+ public static McpResourceReadResult of(String serverName, McpSchema.ReadResourceResult result) {
+ return new McpResourceReadResult(serverName, result.contents());
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpToolCallResult.java b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpToolCallResult.java
new file mode 100644
index 000000000..80bd2d4cb
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpToolCallResult.java
@@ -0,0 +1,27 @@
+package org.dromara.common.mcp.core;
+
+import io.modelcontextprotocol.spec.McpSchema;
+
+import java.util.List;
+
+/**
+ * MCP 工具调用结果。
+ *
+ * @author Lion Li
+ */
+public record McpToolCallResult(
+ String serverName,
+ boolean error,
+ List content,
+ Object structuredContent
+) {
+
+ public static McpToolCallResult of(String serverName, McpSchema.CallToolResult result) {
+ return new McpToolCallResult(
+ serverName,
+ Boolean.TRUE.equals(result.isError()),
+ result.content(),
+ result.structuredContent()
+ );
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-mcp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-mcp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 000000000..cd71c1bbd
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-mcp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+org.dromara.common.mcp.config.McpAutoConfiguration
diff --git a/ruoyi-modules/ruoyi-demo/pom.xml b/ruoyi-modules/ruoyi-demo/pom.xml
index da6105015..847c7d6bf 100644
--- a/ruoyi-modules/ruoyi-demo/pom.xml
+++ b/ruoyi-modules/ruoyi-demo/pom.xml
@@ -98,6 +98,11 @@
ruoyi-common-mqtt
+
+ org.dromara
+ ruoyi-common-mcp
+
+
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
new file mode 100644
index 000000000..163fe7eb9
--- /dev/null
+++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java
@@ -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 演示案例。
+ *
+ * 这些接口不是 MCP 协议接口,只是为了在后台系统中手动触发 MCP Client 调用。
+ * 真实 MCP Server 入口由 Spring AI 挂载到 `/mcp`。
+ *
+ * 调用示例:
+ *
+ * - `GET /demo/mcp/tools`:查看已连接 MCP Server 的工具列表
+ * - `GET /demo/mcp/receive?toolName=demo_get_data&id=1`:调用外部工具并模拟业务处理
+ * - `GET /demo/mcp/resource?uri=demo://summary`:读取外部 MCP 资源
+ *
+ * 当 `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