feat: 完成 Agent MCP 对接
- 增加 MCP 连接类型、环境检测接口和容器运行环境支持 - 将 Agent 编排改为绑定整体 MCP 并编译为 runtime McpSpec - 优化 MCP 工具展示、审批、草稿试运行和画布回显稳定性
This commit is contained in:
@@ -37,6 +37,18 @@ public class McpBase extends DateEntity implements Serializable {
|
||||
@Column(comment = "完整MCP配置JSON")
|
||||
private String configJson;
|
||||
|
||||
/**
|
||||
* MCP连接方式
|
||||
*/
|
||||
@Column(comment = "MCP连接方式")
|
||||
private String transportType;
|
||||
|
||||
/**
|
||||
* 是否启用工具调用审批
|
||||
*/
|
||||
@Column(comment = "是否启用工具调用审批")
|
||||
private Boolean approvalRequired;
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
@@ -111,6 +123,22 @@ public class McpBase extends DateEntity implements Serializable {
|
||||
this.configJson = configJson;
|
||||
}
|
||||
|
||||
public String getTransportType() {
|
||||
return transportType;
|
||||
}
|
||||
|
||||
public void setTransportType(String transportType) {
|
||||
this.transportType = transportType;
|
||||
}
|
||||
|
||||
public Boolean getApprovalRequired() {
|
||||
return approvalRequired;
|
||||
}
|
||||
|
||||
public void setApprovalRequired(Boolean approvalRequired) {
|
||||
this.approvalRequired = approvalRequired;
|
||||
}
|
||||
|
||||
public BigInteger getDeptId() {
|
||||
return deptId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package tech.easyflow.ai.mcp;
|
||||
|
||||
import tech.easyflow.common.util.StringUtil;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* MCP 连接方式。
|
||||
*/
|
||||
public enum McpTransportType {
|
||||
|
||||
/**
|
||||
* 标准输入输出进程通信。
|
||||
*/
|
||||
STDIO("stdio"),
|
||||
|
||||
/**
|
||||
* HTTP SSE 通信。
|
||||
*/
|
||||
SSE("http-sse"),
|
||||
|
||||
/**
|
||||
* Streamable HTTP 通信。
|
||||
*/
|
||||
HTTP("http-stream");
|
||||
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* 创建 MCP 连接方式。
|
||||
*
|
||||
* @param value 配置值
|
||||
*/
|
||||
McpTransportType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置值。
|
||||
*
|
||||
* @return 配置值
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析连接方式。
|
||||
*
|
||||
* @param value 连接方式文本
|
||||
* @return MCP 连接方式
|
||||
*/
|
||||
public static McpTransportType from(String value) {
|
||||
if (StringUtil.noText(value)) {
|
||||
return STDIO;
|
||||
}
|
||||
String normalized = value.trim().toLowerCase(Locale.ROOT);
|
||||
return switch (normalized) {
|
||||
case "stdio" -> STDIO;
|
||||
case "sse", "http-sse" -> SSE;
|
||||
case "http", "http-stream", "streamable-http" -> HTTP;
|
||||
default -> throw new BusinessException("不支持的 MCP 连接方式: " + value);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package tech.easyflow.ai.service;
|
||||
|
||||
import com.easyagents.core.model.chat.tool.Tool;
|
||||
import com.easyagents.mcp.client.McpEnvironmentCheckResult;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.service.IService;
|
||||
import tech.easyflow.ai.entity.BotMcp;
|
||||
@@ -30,4 +31,6 @@ public interface McpService extends IService<Mcp> {
|
||||
Mcp getMcpTools(String id);
|
||||
|
||||
Page<Mcp> pageTools(Page<Mcp> mcpPage);
|
||||
|
||||
McpEnvironmentCheckResult checkMcp(String configJson);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package tech.easyflow.ai.service.impl;
|
||||
import com.easyagents.core.model.chat.tool.Parameter;
|
||||
import com.easyagents.core.model.chat.tool.Tool;
|
||||
import com.easyagents.mcp.client.McpClientManager;
|
||||
import com.easyagents.mcp.client.McpEnvironmentCheckResult;
|
||||
import com.easyagents.mcp.client.McpEnvironmentChecker;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
@@ -16,6 +18,7 @@ import tech.easyflow.ai.easyagents.tool.McpTool;
|
||||
import tech.easyflow.ai.entity.BotMcp;
|
||||
import tech.easyflow.ai.entity.Mcp;
|
||||
import tech.easyflow.ai.mapper.McpMapper;
|
||||
import tech.easyflow.ai.mcp.McpTransportType;
|
||||
import tech.easyflow.ai.service.McpService;
|
||||
import tech.easyflow.ai.utils.CommonFiledUtil;
|
||||
import tech.easyflow.common.constant.enums.EnumRes;
|
||||
@@ -37,7 +40,8 @@ import java.util.*;
|
||||
@Service
|
||||
public class McpServiceImpl extends ServiceImpl<McpMapper, Mcp> implements McpService {
|
||||
private final McpClientManager mcpClientManager = McpClientManager.getInstance();
|
||||
protected Logger Log = LoggerFactory.getLogger(DocumentServiceImpl.class);
|
||||
private final McpEnvironmentChecker mcpEnvironmentChecker = new McpEnvironmentChecker();
|
||||
protected Logger Log = LoggerFactory.getLogger(McpServiceImpl.class);
|
||||
|
||||
@Override
|
||||
public Result<?> saveMcp(Mcp entity) {
|
||||
@@ -49,6 +53,8 @@ public class McpServiceImpl extends ServiceImpl<McpMapper, Mcp> implements McpS
|
||||
if (!StringUtil.hasText(serverName)) {
|
||||
return Result.fail("未找到mcp服务名称", serverName);
|
||||
}
|
||||
entity.setTransportType(getFirstMcpTransportType(entity.getConfigJson()));
|
||||
entity.setApprovalRequired(Boolean.TRUE.equals(entity.getApprovalRequired()));
|
||||
try {
|
||||
mcpClientManager.registerFromJson(entity.getConfigJson());
|
||||
} catch (Exception e) {
|
||||
@@ -79,6 +85,8 @@ public class McpServiceImpl extends ServiceImpl<McpMapper, Mcp> implements McpS
|
||||
if (!StringUtil.hasText(serverName)) {
|
||||
return Result.fail("未找到mcp服务名称", serverName);
|
||||
}
|
||||
entity.setTransportType(getFirstMcpTransportType(entity.getConfigJson()));
|
||||
entity.setApprovalRequired(Boolean.TRUE.equals(entity.getApprovalRequired()));
|
||||
if (entity.getStatus()) {
|
||||
try {
|
||||
mcpClientManager.registerFromJson(entity.getConfigJson());
|
||||
@@ -121,6 +129,7 @@ public class McpServiceImpl extends ServiceImpl<McpMapper, Mcp> implements McpS
|
||||
records.forEach(mcp -> {
|
||||
boolean clientOnline = mcpClientManager.isClientOnline(getFirstMcpServerName(mcp.getConfigJson()));
|
||||
mcp.setClientOnline(clientOnline);
|
||||
mcp.setTransportType(resolveMcpTransportType(mcp));
|
||||
}
|
||||
);
|
||||
page.getData().setRecords(records);
|
||||
@@ -130,6 +139,9 @@ public class McpServiceImpl extends ServiceImpl<McpMapper, Mcp> implements McpS
|
||||
@Override
|
||||
public Mcp getMcpTools(String id) {
|
||||
Mcp mcp = this.getById(id);
|
||||
if (mcp != null) {
|
||||
mcp.setTransportType(resolveMcpTransportType(mcp));
|
||||
}
|
||||
if (mcp != null && mcp.getStatus()) {
|
||||
McpSyncClient mcpClient = getMcpClient(mcp, mcpClientManager);
|
||||
List<McpSchema.Tool> tools = null;
|
||||
@@ -209,9 +221,27 @@ public class McpServiceImpl extends ServiceImpl<McpMapper, Mcp> implements McpS
|
||||
return firstServerName.orElse(null);
|
||||
}
|
||||
|
||||
public static String getFirstMcpTransportType(String mcpJson) {
|
||||
JSONObject rootJson = JSON.parseObject(mcpJson);
|
||||
JSONObject mcpServersJson = rootJson.getJSONObject("mcpServers");
|
||||
if (mcpServersJson == null || mcpServersJson.isEmpty()) {
|
||||
return McpTransportType.STDIO.getValue();
|
||||
}
|
||||
Optional<String> firstServerName = mcpServersJson.keySet().stream().findFirst();
|
||||
if (firstServerName.isEmpty()) {
|
||||
return McpTransportType.STDIO.getValue();
|
||||
}
|
||||
JSONObject serverJson = mcpServersJson.getJSONObject(firstServerName.get());
|
||||
if (serverJson == null) {
|
||||
return McpTransportType.STDIO.getValue();
|
||||
}
|
||||
return McpTransportType.from(serverJson.getString("transport")).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Mcp> pageTools(Page<Mcp> page) {
|
||||
page.getRecords().forEach(mcp -> {
|
||||
mcp.setTransportType(resolveMcpTransportType(mcp));
|
||||
// mcp 未启用,不查询工具
|
||||
if (!mcp.getStatus()) {
|
||||
return;
|
||||
@@ -235,6 +265,11 @@ public class McpServiceImpl extends ServiceImpl<McpMapper, Mcp> implements McpS
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public McpEnvironmentCheckResult checkMcp(String configJson) {
|
||||
return mcpEnvironmentChecker.check(configJson);
|
||||
}
|
||||
|
||||
private Result<?> validateMcpConfig(Mcp entity) {
|
||||
if (entity == null || !StringUtil.hasText(entity.getConfigJson())) {
|
||||
Log.error("MCP 配置不能为空");
|
||||
@@ -242,4 +277,14 @@ public class McpServiceImpl extends ServiceImpl<McpMapper, Mcp> implements McpS
|
||||
}
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
private String resolveMcpTransportType(Mcp mcp) {
|
||||
if (mcp == null) {
|
||||
return McpTransportType.STDIO.getValue();
|
||||
}
|
||||
if (StringUtil.hasText(mcp.getTransportType())) {
|
||||
return McpTransportType.from(mcp.getTransportType()).getValue();
|
||||
}
|
||||
return getFirstMcpTransportType(mcp.getConfigJson());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user