chore: initialize qiming workspace repository
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-mcp</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>app-platform-mcp-application</artifactId>
|
||||
<name>app-platform-mcp-application</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-mcp-domain</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-mcp-sdk</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-agent-core-adapter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.xspaceagi.mcp.application.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class EnNameDto implements Serializable {
|
||||
|
||||
@JsonPropertyDescription("根据用户输入生成一个英文名称,如果输入本身不是中文,则直接返回,如果中文没有任何含义可以返回拼音。不超过32个字符,不能出现空格,使用小写,多个单词用下划线隔开,例如 query_price")
|
||||
private String enName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.xspaceagi.mcp.application.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class McpExecuteResultDto implements Serializable {
|
||||
|
||||
@Schema(description = "执行结果状态")
|
||||
private boolean success;
|
||||
|
||||
@Schema(description = "执行结果")
|
||||
private Object result;
|
||||
|
||||
@Schema(description = "执行日志")
|
||||
private List<String> logs;
|
||||
|
||||
@Schema(description = "执行错误信息")
|
||||
private String error;
|
||||
|
||||
@Schema(description = "请求ID")
|
||||
private String requestId;
|
||||
|
||||
@Schema(description = "请求耗时")
|
||||
private Long costTime;
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.xspaceagi.mcp.application.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.xspaceagi.mcp.adapter.domain.McpConfigDomainService;
|
||||
import com.xspaceagi.mcp.adapter.repository.entity.McpConfig;
|
||||
import com.xspaceagi.mcp.infra.client.McpAsyncClientWrapper;
|
||||
import com.xspaceagi.mcp.infra.rpc.McpDeployRpcService;
|
||||
import com.xspaceagi.mcp.sdk.dto.*;
|
||||
import com.xspaceagi.mcp.sdk.enums.DeployStatusEnum;
|
||||
import com.xspaceagi.mcp.sdk.enums.McpDataTypeEnum;
|
||||
import com.xspaceagi.system.spec.utils.MD5;
|
||||
import io.modelcontextprotocol.spec.McpSchema;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractDeployTaskService {
|
||||
|
||||
@Resource
|
||||
private McpDeployRpcService mcpDeployRpcService;
|
||||
|
||||
@Resource
|
||||
private McpConfigDomainService mcpConfigDomainService;
|
||||
|
||||
protected void updateAndSaveMcpConfig(Long id, String serverConfig, boolean isSSE, Date modified) {
|
||||
McpConfigDto mcpConfigDto = new McpConfigDto();
|
||||
mcpConfigDto.setServerConfig(serverConfig);
|
||||
McpAsyncClientWrapper mcpAsyncClientWrapper = null;
|
||||
try {
|
||||
if (isSSE) {
|
||||
mcpAsyncClientWrapper = mcpDeployRpcService.getMcpAsyncClientForSSE(String.valueOf(id), MD5.MD5Encode(serverConfig), serverConfig).block();
|
||||
} else {
|
||||
mcpAsyncClientWrapper = mcpDeployRpcService.getMcpAsyncClient(String.valueOf(id), MD5.MD5Encode(serverConfig), serverConfig).block();
|
||||
}
|
||||
McpSchema.ListToolsResult toolsResult = mcpAsyncClientWrapper.getClient().listTools().timeout(Duration.ofSeconds(30)).block();
|
||||
if (toolsResult.tools() != null) {
|
||||
mcpConfigDto.setTools(new ArrayList<>());
|
||||
for (McpSchema.Tool tool : toolsResult.tools()) {
|
||||
McpToolDto mcpToolDto = new McpToolDto();
|
||||
mcpToolDto.setName(tool.name());
|
||||
mcpToolDto.setDescription(tool.description());
|
||||
mcpToolDto.setInputArgs(convertInputSchemaToInputArgs(tool.inputSchema().properties(), tool.inputSchema().required()));
|
||||
mcpToolDto.setJsonSchema(JSON.toJSONString(tool.inputSchema()));
|
||||
mcpConfigDto.getTools().add(mcpToolDto);
|
||||
}
|
||||
}
|
||||
try {
|
||||
//数据量可能比较大,后续优化
|
||||
McpSchema.ListResourcesResult resourcesResult = mcpAsyncClientWrapper.getClient().listResources().timeout(Duration.ofSeconds(30)).block();
|
||||
if (resourcesResult != null) {
|
||||
mcpConfigDto.setResources(new ArrayList<>());
|
||||
for (McpSchema.Resource resource : resourcesResult.resources()) {
|
||||
McpResourceDto mcpResourceDto = new McpResourceDto();
|
||||
mcpResourceDto.setUri(resource.uri());
|
||||
mcpResourceDto.setName(resource.name());
|
||||
mcpResourceDto.setDescription(resource.description());
|
||||
mcpResourceDto.setMimeType(resource.mimeType());
|
||||
mcpResourceDto.setAnnotations(convertToAnnotations(resource.annotations()));
|
||||
McpArgDto mcpArgDto = new McpArgDto();
|
||||
mcpArgDto.setName("uri");
|
||||
mcpArgDto.setDescription("uri");
|
||||
mcpArgDto.setBindValue(resource.uri());
|
||||
mcpArgDto.setKey(resource.uri());
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.String);
|
||||
mcpArgDto.setRequire(true);
|
||||
mcpResourceDto.setInputArgs(List.of(mcpArgDto));
|
||||
mcpConfigDto.getResources().add(mcpResourceDto);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("listResources error {}", e.getMessage());
|
||||
// ignore 服务未提供resource功能时会报错
|
||||
}
|
||||
try {
|
||||
McpSchema.ListPromptsResult listPromptsResult = mcpAsyncClientWrapper.getClient().listPrompts().timeout(Duration.ofSeconds(30)).block();
|
||||
if (listPromptsResult != null) {
|
||||
mcpConfigDto.setPrompts(new ArrayList<>());
|
||||
for (McpSchema.Prompt prompt : listPromptsResult.prompts()) {
|
||||
McpPromptDto mcpPromptDto = new McpPromptDto();
|
||||
mcpPromptDto.setName(prompt.name());
|
||||
mcpPromptDto.setDescription(prompt.description());
|
||||
if (prompt.arguments() != null) {
|
||||
mcpPromptDto.setInputArgs(prompt.arguments().stream().map(argument -> {
|
||||
McpArgDto mcpArgDto = new McpArgDto();
|
||||
mcpArgDto.setName(argument.name());
|
||||
mcpArgDto.setKey(argument.name());
|
||||
mcpArgDto.setDescription(argument.description());
|
||||
mcpArgDto.setRequire(argument.required());
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.String);
|
||||
return mcpArgDto;
|
||||
}).collect(Collectors.toList()));
|
||||
}
|
||||
mcpConfigDto.getPrompts().add(mcpPromptDto);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("listPrompts error {}", e.getMessage());
|
||||
// ignore 服务未提供resource功能时会报错
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error("McpDeployRpcServiceImpl.deploy error", e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (mcpAsyncClientWrapper != null) {
|
||||
try {
|
||||
mcpDeployRpcService.closeMcpClient(String.valueOf(id), MD5.MD5Encode(serverConfig), mcpAsyncClientWrapper);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
log.error("McpDeployRpcServiceImpl.deploy closeMcpClient error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
McpConfig mcpConfig = new McpConfig();
|
||||
mcpConfig.setId(id);
|
||||
mcpConfig.setDeployStatus(DeployStatusEnum.Deployed);
|
||||
mcpConfig.setDeployed(new Date());
|
||||
mcpConfig.setConfig(JSON.toJSONString(mcpConfigDto));
|
||||
mcpConfig.setDeployedConfig(JSON.toJSONString(mcpConfigDto));
|
||||
if (modified != null) {
|
||||
mcpConfig.setModified(modified);
|
||||
}
|
||||
mcpConfigDomainService.update(mcpConfig);
|
||||
}
|
||||
|
||||
|
||||
private McpResourceContent.Annotations convertToAnnotations(McpSchema.Annotations annotations) {
|
||||
List<McpSchema.Role> audience = annotations.audience();
|
||||
List<String> audience0 = null;
|
||||
if (audience != null) {
|
||||
audience0 = audience.stream().map(McpSchema.Role::name).collect(Collectors.toList());
|
||||
}
|
||||
return new McpResourceContent.Annotations(audience0, annotations.priority());
|
||||
}
|
||||
|
||||
private List<McpArgDto> convertInputSchemaToInputArgs(Map<String, Object> properties, List<String> requiredArgs) {
|
||||
List<McpArgDto> mcpArgDtos = new ArrayList<>();
|
||||
if (properties == null || properties.size() == 0) {
|
||||
return mcpArgDtos;
|
||||
}
|
||||
List<String> required = new ArrayList<>();
|
||||
if (requiredArgs != null) {
|
||||
required.addAll(requiredArgs);
|
||||
}
|
||||
return properties.entrySet().stream().map(entry -> {
|
||||
McpArgDto mcpArgDto = new McpArgDto();
|
||||
String name = entry.getKey();
|
||||
mcpArgDto.setName(name);
|
||||
mcpArgDto.setKey(name);
|
||||
if (required.contains(name)) {
|
||||
mcpArgDto.setRequire(true);
|
||||
}
|
||||
if (entry.getValue() instanceof Map<?, ?>) {
|
||||
Map<String, Object> value = (Map<String, Object>) entry.getValue();
|
||||
mcpArgDto.setDescription(value.get("description") != null ? value.get("description").toString() : "");
|
||||
mcpArgDto.setBindValue(value.get("default") != null ? value.get("default").toString() : null);
|
||||
if ("object".equals(value.get("type"))) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.Object);
|
||||
mcpArgDto.setSubArgs(convertInputSchemaToInputArgs((Map<String, Object>) value.get("properties"), (List<String>) value.get("required")));
|
||||
} else if ("array".equals(value.get("type"))) {
|
||||
Map<String, Object> itemSchema = (Map<String, Object>) value.get("items");
|
||||
if (itemSchema != null) {
|
||||
if ("object".equals(itemSchema.get("type"))) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.Array_Object);
|
||||
mcpArgDto.setSubArgs(convertInputSchemaToInputArgs((Map<String, Object>) itemSchema.get("properties"), (List<String>) itemSchema.get("required")));
|
||||
} else if ("number".equals(itemSchema.get("type"))) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.Array_Number);
|
||||
} else if ("integer".equals(itemSchema.get("type"))) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.Array_Integer);
|
||||
} else if ("boolean".equals(itemSchema.get("type"))) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.Array_Boolean);
|
||||
} else {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.Array_String);
|
||||
}
|
||||
}
|
||||
} else if ("number".equals(value.get("type"))) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.Number);
|
||||
} else if ("integer".equals(value.get("type"))) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.Integer);
|
||||
} else if ("boolean".equals(value.get("type"))) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.Boolean);
|
||||
} else {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.String);
|
||||
}
|
||||
if (value.get("enum") != null) {
|
||||
List<?> enums = (List<?>) value.get("enum");
|
||||
mcpArgDto.setEnums(enums.stream().map(Object::toString).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
return mcpArgDto;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,718 @@
|
||||
package com.xspaceagi.mcp.application.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.xspaceagi.agent.core.adapter.application.ModelApplicationService;
|
||||
import com.xspaceagi.agent.core.sdk.IAgentRpcService;
|
||||
import com.xspaceagi.agent.core.sdk.dto.*;
|
||||
import com.xspaceagi.agent.core.spec.enums.DataTypeEnum;
|
||||
import com.xspaceagi.compose.sdk.request.DorisTableDefineRequest;
|
||||
import com.xspaceagi.compose.sdk.service.IComposeDbTableRpcService;
|
||||
import com.xspaceagi.compose.sdk.vo.define.TableDefineVo;
|
||||
import com.xspaceagi.compose.sdk.vo.define.TableFieldDefineVo;
|
||||
import com.xspaceagi.knowledge.sdk.response.KnowledgeConfigVo;
|
||||
import com.xspaceagi.knowledge.sdk.sevice.IKnowledgeConfigRpcService;
|
||||
import com.xspaceagi.mcp.adapter.application.McpConfigApplicationService;
|
||||
import com.xspaceagi.mcp.adapter.application.McpDeployTaskService;
|
||||
import com.xspaceagi.mcp.adapter.domain.McpConfigDomainService;
|
||||
import com.xspaceagi.mcp.adapter.dto.McpPageQueryDto;
|
||||
import com.xspaceagi.mcp.adapter.repository.entity.McpConfig;
|
||||
import com.xspaceagi.mcp.application.dto.EnNameDto;
|
||||
import com.xspaceagi.mcp.sdk.dto.*;
|
||||
import com.xspaceagi.mcp.sdk.enums.DeployStatusEnum;
|
||||
import com.xspaceagi.mcp.sdk.enums.InstallTypeEnum;
|
||||
import com.xspaceagi.mcp.sdk.enums.McpComponentTypeEnum;
|
||||
import com.xspaceagi.mcp.sdk.enums.McpDataTypeEnum;
|
||||
import com.xspaceagi.system.application.dto.TenantConfigDto;
|
||||
import com.xspaceagi.system.application.dto.UserDto;
|
||||
import com.xspaceagi.system.application.service.UserApplicationService;
|
||||
import com.xspaceagi.system.application.util.DefaultIconUrlUtil;
|
||||
import com.xspaceagi.system.sdk.service.UserAccessKeyApiService;
|
||||
import com.xspaceagi.system.sdk.service.dto.UserAccessKeyDto;
|
||||
import com.xspaceagi.system.spec.common.RequestContext;
|
||||
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
|
||||
import com.xspaceagi.system.spec.enums.YnEnum;
|
||||
import com.xspaceagi.system.spec.exception.BizException;
|
||||
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class McpConfigApplicationServiceImpl implements McpConfigApplicationService {
|
||||
|
||||
@Resource
|
||||
private McpConfigDomainService mcpConfigDomainService;
|
||||
|
||||
@Resource
|
||||
private UserApplicationService userApplicationService;
|
||||
|
||||
@Resource
|
||||
private IAgentRpcService iAgentRpcService;
|
||||
|
||||
@Resource
|
||||
private IComposeDbTableRpcService iComposeDbTableRpcService;
|
||||
|
||||
@Resource
|
||||
private IKnowledgeConfigRpcService iKnowledgeConfigRpcService;
|
||||
|
||||
@Resource
|
||||
private UserAccessKeyApiService userAccessKeyApiService;
|
||||
|
||||
@Resource
|
||||
private McpDeployTaskService mcpDeployTaskService;
|
||||
|
||||
@Resource
|
||||
private ModelApplicationService modelApplicationService;
|
||||
|
||||
@Override
|
||||
public void addMcp(McpDto mcpDto) {
|
||||
mcpDto.setId(null);
|
||||
McpConfig mcpConfig = new McpConfig();
|
||||
BeanUtils.copyProperties(mcpDto, mcpConfig);
|
||||
completeComponentMcpToolNames(mcpDto);
|
||||
mcpConfig.setConfig(JSON.toJSONString(mcpDto.getMcpConfig()));
|
||||
if (mcpDto.getDeployedConfig() != null) {
|
||||
mcpConfig.setDeployedConfig(JSON.toJSONString(mcpDto.getDeployedConfig()));
|
||||
}
|
||||
mcpConfigDomainService.add(mcpConfig);
|
||||
mcpDto.setId(mcpConfig.getId());
|
||||
}
|
||||
|
||||
private void completeComponentMcpToolNames(McpDto mcpDto) {
|
||||
if (mcpDto.getInstallType() != InstallTypeEnum.COMPONENT) {
|
||||
return;
|
||||
}
|
||||
Map<String, McpComponentDto> componentMap = new HashMap<>();
|
||||
List<String> toolNames = new ArrayList<>();
|
||||
if (mcpDto.getId() != null) {
|
||||
McpConfig mcpConfig = mcpConfigDomainService.getById(mcpDto.getId());
|
||||
McpConfigDto mcpConfigDto = JSON.parseObject(mcpConfig.getConfig(), McpConfigDto.class);
|
||||
if (mcpConfigDto.getComponents() != null) {
|
||||
//type + targetId作为key
|
||||
componentMap = mcpConfigDto.getComponents().stream().collect(Collectors.toMap(componentDto -> componentDto.getType().name() + "_" + componentDto.getTargetId(), McpComponentDto -> McpComponentDto, (a, b) -> a));
|
||||
toolNames = mcpConfigDto.getComponents().stream().map(McpComponentDto::getToolName).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
if (mcpDto.getMcpConfig() != null && mcpDto.getMcpConfig().getComponents() != null) {
|
||||
for (McpComponentDto componentDto : mcpDto.getMcpConfig().getComponents()) {
|
||||
String key = componentDto.getType().name() + "_" + componentDto.getTargetId();
|
||||
McpComponentDto component = componentMap.get(key);
|
||||
//存在则使用
|
||||
if (component != null && StringUtils.isNotBlank(component.getToolName())) {
|
||||
componentDto.setToolName(component.getToolName());
|
||||
} else {
|
||||
EnNameDto enNameDto = null;
|
||||
try {
|
||||
String prompt = componentDto.getName();
|
||||
if (StringUtils.isNotBlank(componentDto.getDescription())) {
|
||||
prompt += "(" + componentDto.getDescription() + ")";
|
||||
}
|
||||
enNameDto = modelApplicationService.call(prompt, new ParameterizedTypeReference<EnNameDto>() {
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.warn("call model name error", e);
|
||||
//忽略
|
||||
}
|
||||
if (enNameDto != null && StringUtils.isNotBlank(enNameDto.getEnName())) {
|
||||
String toolName = enNameDto.getEnName().replace(" ", "");
|
||||
if (toolNames.contains(toolName)) {
|
||||
toolName = toolName + "_" + componentDto.getTargetId();
|
||||
toolNames.add(toolName);
|
||||
}
|
||||
componentDto.setToolName(toolName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMcp(McpDto mcpDto) {
|
||||
Assert.notNull(mcpDto, "mcpDto must be non-null");
|
||||
Assert.notNull(mcpDto.getId(), "id must be non-null");
|
||||
McpDto mcp = getMcp(mcpDto.getId());
|
||||
if (mcp == null) {
|
||||
throw BizException.of(ErrorCodeEnum.INVALID_PARAM, BizExceptionCodeEnum.mcpNotFound);
|
||||
}
|
||||
mcpDto.setInstallType(mcp.getInstallType());
|
||||
completeComponentMcpToolNames(mcpDto);
|
||||
McpConfig mcpConfig = new McpConfig();
|
||||
BeanUtils.copyProperties(mcpDto, mcpConfig);
|
||||
if (mcpDto.getMcpConfig() != null) {
|
||||
mcpConfig.setConfig(JSON.toJSONString(mcpDto.getMcpConfig()));
|
||||
}
|
||||
if (mcpDto.getDeployStatus() == DeployStatusEnum.Stopped) {
|
||||
mcpConfig.setDeployedConfig(null);
|
||||
}
|
||||
if (mcpDto.getIcon() != null && mcpDto.getIcon().contains("api/logo/")) {
|
||||
mcpConfig.setIcon(null);
|
||||
}
|
||||
mcpConfigDomainService.update(mcpConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMcp(Long id) {
|
||||
mcpConfigDomainService.delete(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public McpDto getMcp(Long id) {
|
||||
return getMcp0(id, false);
|
||||
}
|
||||
|
||||
private McpDto getMcp0(Long id, boolean deployed) {
|
||||
McpConfig mcpConfig = mcpConfigDomainService.getById(id);
|
||||
if (mcpConfig == null) {
|
||||
return null;
|
||||
}
|
||||
if (deployed && (mcpConfig.getDeployStatus() == DeployStatusEnum.Initialization || mcpConfig.getDeployStatus() == DeployStatusEnum.Stopped || mcpConfig.getDeployedConfig() == null)) {
|
||||
return null;
|
||||
}
|
||||
McpDto mcpDto = new McpDto();
|
||||
BeanUtils.copyProperties(mcpConfig, mcpDto);
|
||||
mcpDto.setMcpConfig(convertToMcpConfig(mcpConfig.getConfig()));
|
||||
mcpDto.setDeployedConfig(convertToMcpConfig(mcpConfig.getDeployedConfig()));
|
||||
if (mcpDto.getInstallType() == InstallTypeEnum.COMPONENT) {
|
||||
completeComponent(mcpDto.getDeployedConfig(), mcpConfig.getSpaceId());
|
||||
if (mcpDto.getMcpConfig() != null && mcpDto.getDeployStatus() == DeployStatusEnum.Deployed) {
|
||||
mcpDto.getMcpConfig().setTools(mcpDto.getDeployedConfig().getTools());
|
||||
}
|
||||
}
|
||||
UserDto userDto = userApplicationService.queryById(mcpConfig.getCreatorId());
|
||||
CreatorDto creatorDto = convertUserToCreator(userDto);
|
||||
mcpDto.setCreator(creatorDto);
|
||||
mcpDto.setIcon(DefaultIconUrlUtil.setDefaultIconUrl(mcpDto.getIcon(), mcpDto.getName(), "mcp"));
|
||||
return mcpDto;
|
||||
}
|
||||
|
||||
private McpConfigDto convertToMcpConfig(String config) {
|
||||
if (!JSON.isValid(config)) {
|
||||
return null;
|
||||
}
|
||||
McpConfigDto mcpConfigDto = JSON.parseObject(config, McpConfigDto.class);
|
||||
if (mcpConfigDto == null) {
|
||||
return null;
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(mcpConfigDto.getTools())) {
|
||||
mcpConfigDto.getTools().forEach(mcpToolDto -> {
|
||||
completeMcpArgDtos(mcpToolDto.getInputArgs());
|
||||
completeMcpArgDtos(mcpToolDto.getOutputArgs());
|
||||
});
|
||||
}
|
||||
return mcpConfigDto;
|
||||
}
|
||||
|
||||
private void completeMcpArgDtos(List<McpArgDto> inputArgs) {
|
||||
if (CollectionUtils.isNotEmpty(inputArgs)) {
|
||||
inputArgs.forEach(mcpArgDto -> {
|
||||
if (mcpArgDto.getDataType() == null) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.String);
|
||||
}
|
||||
completeMcpArgDtos(mcpArgDto.getSubArgs());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public McpDto getDeployedMcp(Long id) {
|
||||
return getMcp0(id, true);
|
||||
}
|
||||
|
||||
private void completeComponent(McpConfigDto mcpConfig, Long spaceId) {
|
||||
if (mcpConfig != null && CollectionUtils.isNotEmpty(mcpConfig.getComponents())) {
|
||||
mcpConfig.setResources(new ArrayList<>());
|
||||
mcpConfig.setTools(new ArrayList<>());
|
||||
mcpConfig.setPrompts(new ArrayList<>());
|
||||
List<McpComponentDto> components = mcpConfig.getComponents();
|
||||
Iterator<McpComponentDto> ite = components.iterator();
|
||||
while (ite.hasNext()) {
|
||||
McpComponentDto component = ite.next();
|
||||
if (component.getType() == McpComponentTypeEnum.Agent) {
|
||||
ReqResult<AgentInfoDto> agentInfoDtoReqResult = iAgentRpcService.queryPublishedAgentInfo(component.getTargetId());
|
||||
if (!agentInfoDtoReqResult.isSuccess()) {
|
||||
ite.remove();
|
||||
continue;
|
||||
}
|
||||
AgentInfoDto agentInfoDto = agentInfoDtoReqResult.getData();
|
||||
component.setName(agentInfoDto.getName());
|
||||
component.setDescription(agentInfoDto.getDescription());
|
||||
component.setIcon(DefaultIconUrlUtil.setDefaultIconUrl(agentInfoDto.getIcon(), agentInfoDto.getName(), McpComponentTypeEnum.Agent.name()));
|
||||
McpToolDto mcpToolDto = new McpToolDto();
|
||||
mcpToolDto.setName("agent_execute_" + agentInfoDto.getId());
|
||||
if (StringUtils.isNotBlank(component.getToolName())) {
|
||||
mcpToolDto.setName(component.getToolName());
|
||||
}
|
||||
mcpToolDto.setDescription(agentInfoDto.getDescription());
|
||||
List<McpArgDto> inputArgs = new ArrayList<>();
|
||||
mcpToolDto.setInputArgs(inputArgs);
|
||||
mcpToolDto.getInputArgs().add(McpArgDto.builder().key("message").name("message").description("用户提示词消息内容,如果有文档、图片等附件内容可以将相关的URL地址一并放置在用户提示词消息中")
|
||||
.require(true)
|
||||
.dataType(McpDataTypeEnum.String)
|
||||
.build());
|
||||
if (CollectionUtils.isNotEmpty(agentInfoDto.getVariables())) {
|
||||
McpArgDto mcpArgDto = McpArgDto.builder().key("variables").name("variables").require(false)
|
||||
.dataType(McpDataTypeEnum.Object).description("变量参数,格式为JSON对象")
|
||||
.subArgs(convertToMcpArgDtos(agentInfoDto.getVariables())).build();
|
||||
mcpToolDto.getInputArgs().add(mcpArgDto);
|
||||
}
|
||||
mcpToolDto.setJsonSchema(JSON.toJSONString(buildSchema(mcpToolDto.getInputArgs())));
|
||||
mcpConfig.getTools().add(mcpToolDto);
|
||||
component.setToolName(mcpToolDto.getName());
|
||||
}
|
||||
if (component.getType() == McpComponentTypeEnum.Table) {
|
||||
DorisTableDefineRequest dorisTableDefineRequest = new DorisTableDefineRequest();
|
||||
dorisTableDefineRequest.setTableId(component.getTargetId());
|
||||
TableDefineVo tableDefineVo = iComposeDbTableRpcService.queryTableDefinition(dorisTableDefineRequest);
|
||||
if (tableDefineVo == null) {
|
||||
ite.remove();
|
||||
continue;
|
||||
}
|
||||
component.setName(tableDefineVo.getTableName());
|
||||
component.setDescription(tableDefineVo.getTableDescription());
|
||||
component.setIcon(DefaultIconUrlUtil.setDefaultIconUrl(tableDefineVo.getIcon(), tableDefineVo.getTableName(), McpComponentTypeEnum.Table.name()));
|
||||
McpToolDto mcpToolDto = new McpToolDto();
|
||||
mcpToolDto.setName("sql_execute_" + tableDefineVo.getId());
|
||||
if (StringUtils.isNotBlank(component.getToolName())) {
|
||||
mcpToolDto.setName(component.getToolName());
|
||||
}
|
||||
mcpToolDto.setDescription(tableDefineVo.getTableDescription());
|
||||
mcpToolDto.setInputArgs(Arrays.asList(
|
||||
McpArgDto.builder().key("sql").name("sql").description("SQL语句,可以直接执行且符合业务诉求的SQL语句,符合MySQL语法规范。表结构:" + convertArgsToSimpleTableStructure(tableDefineVo.getFieldList()))
|
||||
.require(true)
|
||||
.dataType(McpDataTypeEnum.String)
|
||||
.build())
|
||||
);
|
||||
mcpToolDto.setJsonSchema(JSON.toJSONString(buildSchema(mcpToolDto.getInputArgs())));
|
||||
mcpConfig.getTools().add(mcpToolDto);
|
||||
component.setToolName(mcpToolDto.getName());
|
||||
}
|
||||
if (component.getType() == McpComponentTypeEnum.Knowledge) {
|
||||
KnowledgeConfigVo knowledgeConfigVo = iKnowledgeConfigRpcService.queryKnowledgeConfigById(component.getTargetId());
|
||||
if (knowledgeConfigVo == null) {
|
||||
ite.remove();
|
||||
continue;
|
||||
}
|
||||
component.setName(knowledgeConfigVo.getName());
|
||||
component.setDescription(knowledgeConfigVo.getDescription());
|
||||
component.setIcon(DefaultIconUrlUtil.setDefaultIconUrl(knowledgeConfigVo.getIcon(), knowledgeConfigVo.getName(), McpComponentTypeEnum.Knowledge.name()));
|
||||
McpToolDto mcpToolDto = new McpToolDto();
|
||||
mcpToolDto.setName("knowledge_query_" + knowledgeConfigVo.getId());
|
||||
if (StringUtils.isNotBlank(component.getToolName())) {
|
||||
mcpToolDto.setName(component.getToolName());
|
||||
}
|
||||
mcpToolDto.setDescription(knowledgeConfigVo.getDescription());
|
||||
mcpToolDto.setInputArgs(List.of(
|
||||
McpArgDto.builder().key("query").name("query").description("知识库搜索关键词")
|
||||
.require(true)
|
||||
.dataType(McpDataTypeEnum.String)
|
||||
.build(),
|
||||
McpArgDto.builder().key("topK").name("topK").description("返回topK条数,默认5")
|
||||
.require(false)
|
||||
.dataType(McpDataTypeEnum.Integer)
|
||||
.build(),
|
||||
McpArgDto.builder().key("rawText").name("rawText").description("是否返回原始段落")
|
||||
.require(false)
|
||||
.dataType(McpDataTypeEnum.Boolean)
|
||||
.build()
|
||||
)
|
||||
);
|
||||
mcpToolDto.setJsonSchema(JSON.toJSONString(buildSchema(mcpToolDto.getInputArgs())));
|
||||
mcpConfig.getTools().add(mcpToolDto);
|
||||
component.setToolName(mcpToolDto.getName());
|
||||
}
|
||||
if (component.getType() == McpComponentTypeEnum.Plugin) {
|
||||
ReqResult<PluginInfoDto> publishedPluginInfo = iAgentRpcService.getPublishedPluginInfo(component.getTargetId(), spaceId);
|
||||
if (!publishedPluginInfo.isSuccess() || publishedPluginInfo.getData() == null) {
|
||||
ite.remove();
|
||||
continue;
|
||||
}
|
||||
PluginInfoDto pluginInfoDto = publishedPluginInfo.getData();
|
||||
component.setName(pluginInfoDto.getName());
|
||||
component.setIcon(DefaultIconUrlUtil.setDefaultIconUrl(pluginInfoDto.getIcon(), pluginInfoDto.getName(), McpComponentTypeEnum.Plugin.name()));
|
||||
component.setDescription(pluginInfoDto.getDescription());
|
||||
McpToolDto mcpToolDto = new McpToolDto();
|
||||
mcpToolDto.setName("tool_plugin_" + pluginInfoDto.getId());
|
||||
if (StringUtils.isNotBlank(component.getToolName())) {
|
||||
mcpToolDto.setName(component.getToolName());
|
||||
}
|
||||
mcpToolDto.setDescription(pluginInfoDto.getDescription());
|
||||
if (pluginInfoDto.getInputArgs() != null) {
|
||||
List<ArgDto> bindArgs = null;
|
||||
if (StringUtils.isNotBlank(component.getTargetBindConfig())) {
|
||||
bindArgs = iAgentRpcService.parseAgentPluginBindArgs(component.getTargetBindConfig());
|
||||
}
|
||||
//移除未启用的参数
|
||||
removeDisabledArgs(pluginInfoDto.getInputArgs(), bindArgs);
|
||||
List<McpArgDto> argDtos = convertToMcpArgDtos(pluginInfoDto.getInputArgs());
|
||||
mcpToolDto.setInputArgs(argDtos);
|
||||
}
|
||||
mcpToolDto.setJsonSchema(JSON.toJSONString(buildSchema(mcpToolDto.getInputArgs())));
|
||||
mcpConfig.getTools().add(mcpToolDto);
|
||||
component.setToolName(mcpToolDto.getName());
|
||||
component.setTargetConfig(pluginInfoDto.getConfig());
|
||||
}
|
||||
if (component.getType() == McpComponentTypeEnum.Workflow) {
|
||||
ReqResult<WorkflowInfoDto> publishedWorkflowInfo = iAgentRpcService.getPublishedWorkflowInfo(component.getTargetId(), spaceId);
|
||||
if (!publishedWorkflowInfo.isSuccess() || publishedWorkflowInfo.getData() == null) {
|
||||
ite.remove();
|
||||
continue;
|
||||
}
|
||||
WorkflowInfoDto workflowInfoDto = publishedWorkflowInfo.getData();
|
||||
component.setName(workflowInfoDto.getName());
|
||||
component.setIcon(DefaultIconUrlUtil.setDefaultIconUrl(workflowInfoDto.getIcon(), workflowInfoDto.getName(), McpComponentTypeEnum.Workflow.name()));
|
||||
component.setDescription(workflowInfoDto.getDescription());
|
||||
McpToolDto mcpToolDto = new McpToolDto();
|
||||
mcpToolDto.setName("tool_workflow_" + workflowInfoDto.getId());
|
||||
if (StringUtils.isNotBlank(component.getToolName())) {
|
||||
mcpToolDto.setName(component.getToolName());
|
||||
}
|
||||
if (workflowInfoDto.getInputArgs() != null) {
|
||||
List<ArgDto> bindArgs = null;
|
||||
if (StringUtils.isNotBlank(component.getTargetBindConfig())) {
|
||||
bindArgs = iAgentRpcService.parseWorkflowPluginBindArgs(component.getTargetBindConfig());
|
||||
}
|
||||
//移除未启用的参数
|
||||
removeDisabledArgs(workflowInfoDto.getInputArgs(), bindArgs);
|
||||
List<McpArgDto> argDtos = convertToMcpArgDtos(workflowInfoDto.getInputArgs());
|
||||
mcpToolDto.setInputArgs(argDtos);
|
||||
}
|
||||
mcpToolDto.setDescription(workflowInfoDto.getDescription());
|
||||
mcpToolDto.setJsonSchema(JSON.toJSONString(buildSchema(mcpToolDto.getInputArgs())));
|
||||
mcpConfig.getTools().add(mcpToolDto);
|
||||
component.setToolName(mcpToolDto.getName());
|
||||
component.setTargetConfig(workflowInfoDto.getConfig());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeDisabledArgs(List<ArgDto> inputArgs, List<ArgDto> bindArgs) {
|
||||
Map<String, ArgDto> bindArgMap = null;
|
||||
if (bindArgs != null) {
|
||||
bindArgMap = bindArgs.stream().collect(Collectors.toMap(ArgDto::getName, a -> a, (a, b) -> a));
|
||||
}
|
||||
Map<String, ArgDto> finalBindArgMap = bindArgMap;
|
||||
inputArgs.removeIf(arg -> {
|
||||
if (arg.getEnable() != null && !arg.getEnable()) {
|
||||
return true;
|
||||
}
|
||||
if (finalBindArgMap != null) {
|
||||
ArgDto bindArg = finalBindArgMap.get(arg.getName());
|
||||
return bindArg != null && bindArg.getEnable() != null && !bindArg.getEnable();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
inputArgs.forEach(arg -> {
|
||||
if (arg.getSubArgs() != null && !arg.getSubArgs().isEmpty()) {
|
||||
ArgDto bindArg = finalBindArgMap == null ? null : finalBindArgMap.get(arg.getName());
|
||||
removeDisabledArgs(arg.getSubArgs(), bindArg == null ? null : bindArg.getSubArgs());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<McpArgDto> convertToMcpArgDtos(List<ArgDto> inputArgs) {
|
||||
if (inputArgs == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return inputArgs.stream().map(arg -> {
|
||||
McpArgDto mcpArgDto = new McpArgDto();
|
||||
mcpArgDto.setName(arg.getName());
|
||||
mcpArgDto.setDescription(arg.getDescription());
|
||||
try {
|
||||
if (arg.getDataType().name().startsWith("File")) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.String);
|
||||
} else if (arg.getDataType().name().startsWith("Array_File")) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.Array_String);
|
||||
} else {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.valueOf(arg.getDataType().name()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
mcpArgDto.setDataType(McpDataTypeEnum.String);
|
||||
}
|
||||
mcpArgDto.setRequire(arg.isRequire());
|
||||
mcpArgDto.setKey(arg.getKey());
|
||||
mcpArgDto.setSubArgs(convertToMcpArgDtos(arg.getSubArgs()));
|
||||
return mcpArgDto;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private CreatorDto convertUserToCreator(UserDto userDto) {
|
||||
if (userDto == null) {
|
||||
return null;
|
||||
}
|
||||
return CreatorDto.builder()
|
||||
.userId(userDto.getId())
|
||||
.userName(userDto.getUserName())
|
||||
.nickName(userDto.getNickName())
|
||||
.avatar(userDto.getAvatar())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<McpDto> queryMcpListBySpaceId(Long spaceId) {
|
||||
List<McpDto> mcpDtos = mcpConfigDomainService.queryListBySpaceId(spaceId).stream().map(mcpConfig -> {
|
||||
boolean isPlatformMcp = mcpConfig.getConfig() != null && mcpConfig.getConfig().contains("TENANT_SECRET");
|
||||
mcpConfig.setDeployedConfig(null);
|
||||
mcpConfig.setConfig(null);
|
||||
McpDto mcpDto = new McpDto();
|
||||
BeanUtils.copyProperties(mcpConfig, mcpDto);
|
||||
mcpDto.setPlatformMcp(isPlatformMcp);
|
||||
mcpDto.setIcon(DefaultIconUrlUtil.setDefaultIconUrl(mcpDto.getIcon(), mcpDto.getName(), "mcp"));
|
||||
return mcpDto;
|
||||
}).collect(Collectors.toList());
|
||||
completeCreator(mcpDtos);
|
||||
return mcpDtos;
|
||||
}
|
||||
|
||||
private void completeCreator(List<McpDto> mcpDtos) {
|
||||
List<UserDto> userDtos = userApplicationService.queryUserListByIds(mcpDtos.stream().map(McpDto::getCreatorId).collect(Collectors.toList()));
|
||||
Map<Long, UserDto> userMap = userDtos.stream().collect(Collectors.toMap(UserDto::getId, userDto -> userDto));
|
||||
TenantConfigDto tenantConfigDto = (TenantConfigDto) RequestContext.get().getTenantConfig();
|
||||
mcpDtos.forEach(mcpDto -> {
|
||||
UserDto userDto = userMap.get(mcpDto.getCreatorId());
|
||||
if (userDto == null) {
|
||||
userDto = new UserDto();
|
||||
userDto.setId(-1L);
|
||||
userDto.setUserName("");
|
||||
}
|
||||
if (StringUtils.isBlank(userDto.getNickName())) {
|
||||
userDto.setNickName(userDto.getUserName());
|
||||
}
|
||||
mcpDto.setCreator(CreatorDto.builder()
|
||||
.userId(userDto.getId())
|
||||
.userName(userDto.getUserName())
|
||||
.nickName(StringUtils.isNotBlank(tenantConfigDto.getOfficialUserName()) && mcpDto.getSpaceId() != null && mcpDto.getSpaceId() == -1L ? tenantConfigDto.getOfficialUserName() : userDto == null ? "" : userDto.getNickName())
|
||||
.avatar(userDto.getAvatar())
|
||||
.build());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<McpDto> queryDeployedMcpList(McpPageQueryDto mcpPageQueryDto) {
|
||||
IPage<McpDto> mcpPage = mcpConfigDomainService.queryDeployedMcpList(mcpPageQueryDto).convert(mcpConfig -> convertToMcpDto(mcpConfig));
|
||||
List<McpDto> mcpDtos = mcpPage.getRecords();
|
||||
completeCreator(mcpDtos);
|
||||
return mcpPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<McpDto> queryDeployedMcpListForManage(McpPageQueryDto mcpPageQueryDto) {
|
||||
IPage<McpDto> mcpPage = mcpConfigDomainService.queryDeployedMcpListForManage(mcpPageQueryDto).convert(mcpConfig -> convertToMcpDto(mcpConfig));
|
||||
List<McpDto> mcpDtos = mcpPage.getRecords();
|
||||
completeCreator(mcpDtos);
|
||||
return mcpPage;
|
||||
}
|
||||
|
||||
private McpDto convertToMcpDto(McpConfig mcpConfig) {
|
||||
McpDto mcpDto = new McpDto();
|
||||
BeanUtils.copyProperties(mcpConfig, mcpDto);
|
||||
mcpDto.setIcon(DefaultIconUrlUtil.setDefaultIconUrl(mcpDto.getIcon(), mcpDto.getName(), "mcp"));
|
||||
mcpDto.setDeployedConfig(JSON.parseObject(mcpConfig.getDeployedConfig(), McpConfigDto.class));
|
||||
if (mcpDto.getDeployedConfig() != null) {
|
||||
mcpDto.getDeployedConfig().setServerConfig(null);
|
||||
mcpDto.getDeployedConfig().setResources(null);
|
||||
mcpDto.getDeployedConfig().setPrompts(null);
|
||||
if (mcpDto.getDeployedConfig().getTools() != null) {
|
||||
mcpDto.getDeployedConfig().getTools().forEach(tool -> {
|
||||
tool.setInputArgs(null);
|
||||
tool.setJsonSchema(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (mcpDto.getInstallType() == InstallTypeEnum.COMPONENT) {
|
||||
completeComponent(mcpDto.getDeployedConfig(), mcpConfig.getSpaceId());
|
||||
mcpDto.getDeployedConfig().setComponents(null);
|
||||
}
|
||||
return mcpDto;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getExportMcpServerConfig(Long userId, Long mcpId, UserAccessKeyDto.UserAccessKeyConfig userAccessKeyConfig) {
|
||||
List<UserAccessKeyDto> userAccessKeyDtos = userAccessKeyApiService.queryAccessKeyList(userId, UserAccessKeyDto.AKTargetType.Mcp, mcpId.toString());
|
||||
UserAccessKeyDto userAccessKeyDto;
|
||||
if (CollectionUtils.isEmpty(userAccessKeyDtos)) {
|
||||
userAccessKeyDto = userAccessKeyApiService.newAccessKey(userId, UserAccessKeyDto.AKTargetType.Mcp, mcpId.toString());
|
||||
} else {
|
||||
userAccessKeyDto = userAccessKeyDtos.get(0);
|
||||
}
|
||||
if (userAccessKeyConfig != null) {
|
||||
userAccessKeyApiService.updateUserAccessKeyConfig(userAccessKeyDto.getId(), userAccessKeyConfig);
|
||||
}
|
||||
McpDto mcpDto = getMcp(mcpId);
|
||||
TenantConfigDto tenantConfigDto = (TenantConfigDto) RequestContext.get().getTenantConfig();
|
||||
String siteUrl = tenantConfigDto.getSiteUrl().trim().endsWith("/") ? tenantConfigDto.getSiteUrl() : tenantConfigDto.getSiteUrl() + "/";
|
||||
String sseUrl = siteUrl + "api/mcp/sse?ak=" + userAccessKeyDto.getAccessKey();
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
JSONObject mcpServers = new JSONObject();
|
||||
JSONObject mcpServer = new JSONObject();
|
||||
jsonObject.put("mcpServers", mcpServers);
|
||||
if (StringUtils.isBlank(mcpDto.getServerName())) {
|
||||
mcpServers.put("mcp-server-" + mcpId, mcpServer);
|
||||
} else {
|
||||
mcpServers.put(mcpDto.getServerName(), mcpServer);
|
||||
}
|
||||
mcpServer.put("type", "sse");
|
||||
mcpServer.put("url", sseUrl);
|
||||
return jsonObject.toJSONString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refreshExportMcpServerConfig(Long userId, Long mcpId) {
|
||||
List<UserAccessKeyDto> userAccessKeyDtos = userAccessKeyApiService.queryAccessKeyList(userId, UserAccessKeyDto.AKTargetType.Mcp, mcpId.toString());
|
||||
if (CollectionUtils.isNotEmpty(userAccessKeyDtos)) {
|
||||
userAccessKeyApiService.refreshAccessKey(userAccessKeyDtos.get(0).getId());
|
||||
}
|
||||
return getExportMcpServerConfig(userId, mcpId, null);
|
||||
}
|
||||
|
||||
public static String convertArgsToSimpleTableStructure(List<TableFieldDefineVo> fieldDefineVos) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (fieldDefineVos == null) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append("CREATE TABLE IF NOT EXISTS `custom_table` (");
|
||||
fieldDefineVos.forEach(arg -> {
|
||||
sb.append(arg.getFieldName()).append(" ").append(getMysqlType(getDataType(arg.getFieldType()))).append(" ");
|
||||
Boolean require = YnEnum.Y.getKey().equals(arg.getNullableFlag()) ? false : true;
|
||||
if (require) {
|
||||
sb.append("NOT NULL ");
|
||||
}
|
||||
sb.append("COMMENT '").append(arg.getFieldDescription().replace("'", "\\'")).append("'");
|
||||
sb.append(",");
|
||||
});
|
||||
return sb.substring(0, sb.length() - 1) + ")";
|
||||
}
|
||||
|
||||
private static DataTypeEnum getDataType(Integer fieldType) {
|
||||
//1:String;2:Integer;3:Number;4:Boolean;5:Date
|
||||
switch (fieldType) {
|
||||
case 2:
|
||||
return DataTypeEnum.Integer;
|
||||
case 3:
|
||||
return DataTypeEnum.Number;
|
||||
case 4:
|
||||
return DataTypeEnum.Boolean;
|
||||
default:
|
||||
return DataTypeEnum.String;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getMysqlType(DataTypeEnum dataType) {
|
||||
switch (dataType) {
|
||||
case String:
|
||||
return "varchar(255)";
|
||||
case Integer:
|
||||
return "int(10)";
|
||||
case Number:
|
||||
return "bigDecimal";
|
||||
case Boolean:
|
||||
return "tinyint(4)";
|
||||
}
|
||||
return "datetime";
|
||||
}
|
||||
|
||||
private static Map<String, Object> buildSchema(List<McpArgDto> inputArgs) {
|
||||
if (inputArgs == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
List<String> required = new ArrayList<>();
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.putAll(Map.of("type", "object", "properties", properties, "required", required));
|
||||
if (inputArgs != null && inputArgs.size() > 0) {
|
||||
for (McpArgDto inputArg : inputArgs) {
|
||||
if (inputArg.isRequire()) {
|
||||
required.add(inputArg.getName());
|
||||
}
|
||||
McpDataTypeEnum dataType = inputArg.getDataType();
|
||||
if (dataType == McpDataTypeEnum.Object) {
|
||||
properties.put(inputArg.getName(), buildSchema(inputArg.getSubArgs()));
|
||||
continue;
|
||||
}
|
||||
if (dataType == McpDataTypeEnum.Array_Object) {
|
||||
properties.put(inputArg.getName(), Map.of("type", "array",
|
||||
"items", buildSchema(inputArg.getSubArgs())));
|
||||
continue;
|
||||
}
|
||||
if (dataType.name().startsWith("Array_String") || dataType.name().startsWith("Array_Number")
|
||||
|| dataType.name().startsWith("Array_Boolean") || dataType.name().startsWith("Array_Integer")) {
|
||||
properties.put(inputArg.getName(), Map.of("type", "array",
|
||||
"items", Map.of("type", inputArg.getDataType().name().split("_")[1].toLowerCase())));
|
||||
continue;
|
||||
}
|
||||
if (inputArg.getDataType().name().startsWith("Array")) {
|
||||
properties.put(inputArg.getName(), Map.of("type", "array", "items", Map.of("type", "string")));
|
||||
continue;
|
||||
}
|
||||
|
||||
String dataTypeStr = McpDataTypeEnum.String.name().toLowerCase();
|
||||
if (inputArg.getDataType() == McpDataTypeEnum.Number) {
|
||||
dataTypeStr = DataTypeEnum.Number.name().toLowerCase();
|
||||
} else if (inputArg.getDataType() == McpDataTypeEnum.Boolean) {
|
||||
dataTypeStr = DataTypeEnum.Boolean.name().toLowerCase();
|
||||
} else if (inputArg.getDataType() == McpDataTypeEnum.Integer) {
|
||||
dataTypeStr = DataTypeEnum.Integer.name().toLowerCase();
|
||||
}
|
||||
properties.put(inputArg.getName(), Map.of("type", dataTypeStr, "description", inputArg.getDescription() == null ? inputArg.getName() : inputArg.getDescription()));
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long deployOfficialMcp(McpDto mcpDto) {
|
||||
Assert.notNull(mcpDto.getUid(), "uid must be non-null");
|
||||
mcpDto.setSpaceId(-1L);
|
||||
McpConfig mcpConfig = mcpConfigDomainService.getBySpaceIdAndUid(-1L, mcpDto.getUid());
|
||||
Long mcpId;
|
||||
if (mcpConfig != null) {
|
||||
mcpDto.setId(mcpConfig.getId());
|
||||
updateMcp(mcpDto);
|
||||
mcpDto.setId(mcpConfig.getId());
|
||||
mcpId = mcpConfig.getId();
|
||||
} else {
|
||||
addMcp(mcpDto);
|
||||
mcpId = mcpDto.getId();
|
||||
}
|
||||
mcpDeployTaskService.addDeployTask(mcpDto);
|
||||
return mcpId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopOfficialMcp(Long id) {
|
||||
McpDto update = new McpDto();
|
||||
update.setId(id);
|
||||
update.setDeployStatus(DeployStatusEnum.Stopped);
|
||||
updateMcp(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long deployProxyMcp(McpDto mcpDto) {
|
||||
Long id = mcpDto.getId();
|
||||
if (mcpDto.getId() == null) {
|
||||
addMcp(mcpDto);
|
||||
id = mcpDto.getId();
|
||||
} else {
|
||||
updateMcp(mcpDto);
|
||||
}
|
||||
mcpDeployTaskService.addDeployTask(mcpDto, false);
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countTotalMcps() {
|
||||
return mcpConfigDomainService.countTotalMcps();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package com.xspaceagi.mcp.application.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.xspaceagi.mcp.adapter.application.McpDeployTaskService;
|
||||
import com.xspaceagi.mcp.adapter.domain.McpConfigDomainService;
|
||||
import com.xspaceagi.mcp.adapter.repository.entity.McpConfig;
|
||||
import com.xspaceagi.mcp.infra.rpc.McpDeployRpcService;
|
||||
import com.xspaceagi.mcp.infra.rpc.dto.McpDeployStatusResponse;
|
||||
import com.xspaceagi.mcp.infra.rpc.enums.McpDeployStatusEnum;
|
||||
import com.xspaceagi.mcp.infra.rpc.enums.McpPersistentTypeEnum;
|
||||
import com.xspaceagi.mcp.sdk.dto.McpDto;
|
||||
import com.xspaceagi.mcp.sdk.enums.DeployStatusEnum;
|
||||
import com.xspaceagi.mcp.sdk.enums.InstallTypeEnum;
|
||||
import com.xspaceagi.system.application.dto.SendNotifyMessageDto;
|
||||
import com.xspaceagi.system.application.dto.UserDto;
|
||||
import com.xspaceagi.system.application.service.NotifyMessageApplicationService;
|
||||
import com.xspaceagi.system.application.service.UserApplicationService;
|
||||
import com.xspaceagi.system.infra.dao.entity.NotifyMessage;
|
||||
import com.xspaceagi.system.sdk.service.ScheduleTaskApiService;
|
||||
import com.xspaceagi.system.sdk.service.TaskExecuteService;
|
||||
import com.xspaceagi.system.sdk.service.dto.ScheduleTaskDto;
|
||||
import com.xspaceagi.system.spec.common.RequestContext;
|
||||
import com.xspaceagi.system.spec.utils.I18nUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service("mcpDeployTaskService")
|
||||
public class McpDeployTaskServiceImpl extends AbstractDeployTaskService implements McpDeployTaskService, TaskExecuteService {
|
||||
|
||||
private static final Long MAX_EXECUTE_TIMES = 60L;
|
||||
|
||||
@Resource
|
||||
private ScheduleTaskApiService scheduleTaskApiService;
|
||||
|
||||
@Resource
|
||||
private McpDeployRpcService mcpDeployRpcService;
|
||||
|
||||
@Resource
|
||||
private McpConfigDomainService mcpConfigDomainService;
|
||||
|
||||
@Resource
|
||||
private NotifyMessageApplicationService notifyMessageApplicationService;
|
||||
|
||||
@Resource
|
||||
private UserApplicationService userApplicationService;
|
||||
|
||||
@Override
|
||||
public void addDeployTask(McpDto mcpDto) {
|
||||
addDeployTask(mcpDto, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDeployTask(McpDto mcpDto, boolean notify) {
|
||||
if (mcpDto.getInstallType() == InstallTypeEnum.COMPONENT) {
|
||||
McpConfig mcpConfig = new McpConfig();
|
||||
mcpConfig.setId(mcpDto.getId());
|
||||
mcpConfig.setConfig(JSON.toJSONString(mcpDto.getMcpConfig()));
|
||||
mcpConfig.setDeployedConfig(JSON.toJSONString(mcpDto.getMcpConfig()));
|
||||
mcpConfig.setDeployStatus(DeployStatusEnum.Deployed);
|
||||
mcpConfig.setDeployed(new Date());
|
||||
mcpConfigDomainService.update(mcpConfig);
|
||||
if (notify) {
|
||||
notifyMessageApplicationService.sendNotifyMessage(SendNotifyMessageDto.builder()
|
||||
.scope(NotifyMessage.MessageScope.System)
|
||||
.content(I18nUtil.systemMessage("Backend.Mcp.Deploy.Success", mcpDto.getName()))
|
||||
.userIds(Arrays.asList(mcpDto.getCreatorId()))
|
||||
.build());
|
||||
}
|
||||
return;
|
||||
}
|
||||
String taskId = "mcp:" + mcpDto.getId() + ":" + System.currentTimeMillis();
|
||||
long maxExecuteTimes = MAX_EXECUTE_TIMES;
|
||||
if (mcpDto.getInstallType() == InstallTypeEnum.SSE || mcpDto.getInstallType() == InstallTypeEnum.STREAMABLE_HTTP) {
|
||||
maxExecuteTimes = 5;
|
||||
}
|
||||
scheduleTaskApiService.start(ScheduleTaskDto.builder()
|
||||
.taskId(taskId)
|
||||
.beanId("mcpDeployTaskService")
|
||||
.maxExecTimes(maxExecuteTimes)
|
||||
.cron(ScheduleTaskDto.Cron.EVERY_10_SECOND.getCron())
|
||||
.params(Map.of("id", mcpDto.getId(),
|
||||
"tenantId", RequestContext.get().getTenantId(),
|
||||
"userId", mcpDto.getCreatorId(),
|
||||
"name", mcpDto.getName(),
|
||||
"description", mcpDto.getDescription(),
|
||||
"installType", mcpDto.getInstallType().name(),
|
||||
"mcpConfig", mcpDto.getMcpConfig().getServerConfig()))
|
||||
.build());
|
||||
McpConfig mcpConfig = new McpConfig();
|
||||
mcpConfig.setId(mcpDto.getId());
|
||||
mcpConfig.setDeployStatus(DeployStatusEnum.Deploying);
|
||||
mcpConfigDomainService.update(mcpConfig);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> asyncExecute(ScheduleTaskDto scheduleTask) {
|
||||
return Mono.fromCallable(() -> execute1(scheduleTask));
|
||||
}
|
||||
|
||||
public boolean execute1(ScheduleTaskDto scheduleTask) {
|
||||
log.info("Deploy MCP {}", scheduleTask.getParams());
|
||||
Map<String, Object> param = (Map<String, Object>) scheduleTask.getParams();
|
||||
if (param.get("tenantId") == null) {
|
||||
return true;
|
||||
}
|
||||
Long tenantId = Long.parseLong(param.get("tenantId").toString());
|
||||
RequestContext.set(RequestContext.builder()
|
||||
.tenantId(tenantId)
|
||||
.build());
|
||||
try {
|
||||
return execute0(scheduleTask);
|
||||
} finally {
|
||||
RequestContext.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean execute0(ScheduleTaskDto scheduleTask) {
|
||||
log.info("Deploy MCP {}", scheduleTask.getParams());
|
||||
Map<String, Object> param = (Map<String, Object>) scheduleTask.getParams();
|
||||
Long id = Long.parseLong(param.get("id").toString());
|
||||
Long userId = Long.parseLong(param.get("userId").toString());
|
||||
String serverConfig = param.get("mcpConfig").toString();
|
||||
String mcpName = param.get("name").toString();
|
||||
boolean isSSE = "SSE".equals(param.get("installType"));
|
||||
UserDto userDto = userApplicationService.queryById(userId);
|
||||
if (userDto == null) {
|
||||
return true;
|
||||
}
|
||||
McpDeployStatusResponse deployStatus;
|
||||
if (isSSE) {
|
||||
deployStatus = new McpDeployStatusResponse();
|
||||
deployStatus.setStatus(McpDeployStatusEnum.Ready);
|
||||
} else {
|
||||
deployStatus = mcpDeployRpcService.deploy(String.valueOf(id), serverConfig, McpPersistentTypeEnum.OneShot);
|
||||
}
|
||||
if (deployStatus.getStatus() == McpDeployStatusEnum.Ready) {
|
||||
try {
|
||||
updateAndSaveMcpConfig(id, serverConfig, isSSE, null);
|
||||
notifyMessageApplicationService.sendNotifyMessage(SendNotifyMessageDto.builder()
|
||||
.scope(NotifyMessage.MessageScope.System)
|
||||
.content(I18nUtil.systemMessage(userDto.getLangMap(), "Backend.Mcp.Deploy.Success", mcpName))
|
||||
.userIds(List.of(userId))
|
||||
.build());
|
||||
|
||||
log.info("MCP service [{}] deployed successfully", mcpName);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
if (isSSE && scheduleTask.getExecTimes() + 1 >= scheduleTask.getMaxExecTimes()) {
|
||||
deployStatus.setMessage(e.getMessage());
|
||||
deployStatus.setStatus(McpDeployStatusEnum.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deployStatus.getStatus() == McpDeployStatusEnum.Error) {
|
||||
McpConfig mcpConfig = new McpConfig();
|
||||
mcpConfig.setId(id);
|
||||
mcpConfig.setDeployStatus(DeployStatusEnum.DeployFailed);
|
||||
mcpConfigDomainService.update(mcpConfig);
|
||||
notifyMessageApplicationService.sendNotifyMessage(SendNotifyMessageDto.builder()
|
||||
.scope(NotifyMessage.MessageScope.System)
|
||||
.content(I18nUtil.systemMessage(userDto.getLangMap(), "Backend.Mcp.Deploy.Failed", mcpName, deployStatus.getMessage()))
|
||||
.userIds(List.of(userId))
|
||||
.build());
|
||||
return true;
|
||||
}
|
||||
checkAndSendFailedNotifyMessage(userDto, id, mcpName, scheduleTask);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkAndSendFailedNotifyMessage(UserDto userDto, Long id, String mcpName, ScheduleTaskDto scheduleTask) {
|
||||
if (scheduleTask.getExecTimes() + 1 >= scheduleTask.getMaxExecTimes()) {
|
||||
McpConfig mcpConfig = new McpConfig();
|
||||
mcpConfig.setId(id);
|
||||
mcpConfig.setDeployStatus(DeployStatusEnum.DeployFailed);
|
||||
mcpConfigDomainService.update(mcpConfig);
|
||||
notifyMessageApplicationService.sendNotifyMessage(SendNotifyMessageDto.builder()
|
||||
.scope(NotifyMessage.MessageScope.System)
|
||||
.content(I18nUtil.systemMessage(userDto.getLangMap(), "Backend.Mcp.Deploy.Timeout", mcpName))
|
||||
.userIds(List.of(userDto.getId()))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.xspaceagi.mcp.application.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.xspaceagi.mcp.adapter.domain.McpConfigDomainService;
|
||||
import com.xspaceagi.mcp.adapter.repository.entity.McpConfig;
|
||||
import com.xspaceagi.mcp.sdk.dto.McpConfigDto;
|
||||
import com.xspaceagi.system.sdk.service.ScheduleTaskApiService;
|
||||
import com.xspaceagi.system.sdk.service.TaskExecuteService;
|
||||
import com.xspaceagi.system.sdk.service.dto.ScheduleTaskDto;
|
||||
import com.xspaceagi.system.spec.common.RequestContext;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service("sseMcpUpdateTaskService")
|
||||
public class SSEMcpUpdateTaskServiceImpl extends AbstractDeployTaskService implements TaskExecuteService {
|
||||
|
||||
|
||||
@Resource
|
||||
private ScheduleTaskApiService scheduleTaskApiService;
|
||||
|
||||
@Resource
|
||||
private McpConfigDomainService mcpConfigDomainService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
scheduleTaskApiService.start(ScheduleTaskDto.builder()
|
||||
.taskId("sseMcpUpdateTaskService")
|
||||
.beanId("sseMcpUpdateTaskService")
|
||||
.maxExecTimes(Long.MAX_VALUE)
|
||||
.cron(ScheduleTaskDto.Cron.EVERYDAY_0_2.getCron())
|
||||
.params(Map.of())
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> asyncExecute(ScheduleTaskDto scheduleTask) {
|
||||
return Mono.fromCallable(() -> execute0(scheduleTask));
|
||||
}
|
||||
|
||||
public boolean execute0(ScheduleTaskDto scheduleTask) {
|
||||
log.info("SSE update task running");
|
||||
Long id = 0L;
|
||||
List<McpConfig> mcpConfigs = mcpConfigDomainService.queryDeployedSSEMcpConfigList(id, 100);
|
||||
while (!mcpConfigs.isEmpty()) {
|
||||
for (McpConfig mcpConfig : mcpConfigs) {
|
||||
McpConfigDto mcpConfigDto = JSON.parseObject(mcpConfig.getConfig(), McpConfigDto.class);
|
||||
try {
|
||||
RequestContext.set(RequestContext.builder()
|
||||
.tenantId(mcpConfig.getTenantId())
|
||||
.build());
|
||||
updateAndSaveMcpConfig(mcpConfig.getId(), mcpConfigDto.getServerConfig(), true, mcpConfig.getModified());
|
||||
log.info("SSE config updated {}, {}, {}", mcpConfig.getId(), mcpConfig.getName(), mcpConfigDto.getServerConfig());
|
||||
} catch (Exception e) {
|
||||
log.error("SSE config update failed {}", mcpConfigDto.getServerConfig(), e);
|
||||
} finally {
|
||||
RequestContext.remove();
|
||||
}
|
||||
id = mcpConfig.getId();
|
||||
}
|
||||
mcpConfigs = mcpConfigDomainService.queryDeployedSSEMcpConfigList(id, 100);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user