package com.vincent.rsf.server.ai.service.mcp;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.vincent.rsf.server.ai.constant.AiMcpConstants;
|
import com.vincent.rsf.server.ai.constant.AiSceneCode;
|
import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
|
import com.vincent.rsf.server.ai.model.AiMcpToolDescriptor;
|
import com.vincent.rsf.server.ai.model.AiPromptContext;
|
import com.vincent.rsf.server.ai.service.provider.AiDiagnosticDataProvider;
|
import com.vincent.rsf.server.system.entity.AiDiagnosticToolConfig;
|
import com.vincent.rsf.server.system.entity.AiMcpMount;
|
import com.vincent.rsf.server.system.service.AiDiagnosticToolConfigService;
|
import com.vincent.rsf.server.system.service.AiMcpMountService;
|
import org.springframework.stereotype.Service;
|
|
import javax.annotation.Resource;
|
import java.util.ArrayList;
|
import java.util.Comparator;
|
import java.util.Date;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.concurrent.ConcurrentHashMap;
|
|
@Service
|
public class AiMcpRegistryService {
|
|
private static final long EXTERNAL_TOOL_CACHE_TTL_MS = 30000L;
|
|
private final List<AiDiagnosticDataProvider> providers;
|
private final Map<String, CachedTools> externalToolCache = new ConcurrentHashMap<>();
|
|
@Resource
|
private AiMcpMountService aiMcpMountService;
|
@Resource
|
private AiDiagnosticToolConfigService aiDiagnosticToolConfigService;
|
@Resource
|
private AiMcpHttpClient aiMcpHttpClient;
|
@Resource
|
private AiMcpSseClient aiMcpSseClient;
|
@Resource
|
private AiMcpPayloadMapper aiMcpPayloadMapper;
|
|
public AiMcpRegistryService(List<AiDiagnosticDataProvider> providers) {
|
this.providers = providers == null ? new ArrayList<>() : providers;
|
}
|
|
/**
|
* 枚举租户下可见的 MCP 工具目录。
|
* 包括内部工具和所有启用中的外部挂载工具。
|
*/
|
public List<AiMcpToolDescriptor> listTools(Long tenantId, Long mountId) {
|
List<AiMcpToolDescriptor> output = new ArrayList<>();
|
List<AiMcpMount> mounts;
|
if (mountId == null) {
|
mounts = aiMcpMountService.list(new LambdaQueryWrapper<AiMcpMount>()
|
.eq(AiMcpMount::getTenantId, tenantId)
|
.eq(AiMcpMount::getEnabledFlag, 1)
|
.eq(AiMcpMount::getStatus, 1)
|
.orderByAsc(AiMcpMount::getMountCode, AiMcpMount::getId));
|
} else {
|
mounts = new ArrayList<>();
|
AiMcpMount mount = aiMcpMountService.getTenantMount(tenantId, mountId);
|
if (mount != null && Integer.valueOf(1).equals(mount.getEnabledFlag()) && Integer.valueOf(1).equals(mount.getStatus())) {
|
mounts.add(mount);
|
}
|
}
|
for (AiMcpMount mount : mounts) {
|
try {
|
if (AiMcpConstants.TRANSPORT_INTERNAL.equalsIgnoreCase(mount.getTransportType())) {
|
output.addAll(buildInternalTools(tenantId, mount));
|
} else if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(mount.getTransportType())) {
|
output.addAll(loadCachedExternalTools(tenantId, mount));
|
} else if (AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(mount.getTransportType())) {
|
output.addAll(loadCachedExternalTools(tenantId, mount));
|
}
|
} catch (Exception ignore) {
|
}
|
}
|
return output;
|
}
|
|
/**
|
* 仅返回系统内置工具目录。
|
*/
|
public List<AiMcpToolDescriptor> listInternalTools(Long tenantId) {
|
AiMcpMount mount = aiMcpMountService.getTenantMountByCode(tenantId, AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE);
|
if (mount == null || !Integer.valueOf(1).equals(mount.getEnabledFlag()) || !Integer.valueOf(1).equals(mount.getStatus())) {
|
return new ArrayList<>();
|
}
|
return buildInternalTools(tenantId, mount);
|
}
|
|
/**
|
* 测试指定挂载的连通性与工具发现能力,并把结果回写到挂载测试状态字段。
|
*/
|
public Map<String, Object> testMount(Long tenantId, Long mountId) {
|
AiMcpMount mount = aiMcpMountService.getTenantMount(tenantId, mountId);
|
if (mount == null) {
|
throw new IllegalArgumentException("MCP挂载不存在");
|
}
|
Map<String, Object> payload = new LinkedHashMap<>();
|
payload.put("mountCode", mount.getMountCode());
|
payload.put("transportType", mount.getTransportType());
|
if (AiMcpConstants.TRANSPORT_INTERNAL.equalsIgnoreCase(mount.getTransportType())) {
|
List<AiMcpToolDescriptor> tools = buildInternalTools(tenantId, mount);
|
payload.put("success", true);
|
payload.put("toolCount", tools.size());
|
payload.put("tools", tools);
|
updateTestState(mount, 1, "内部工具挂载正常", tools.size());
|
return payload;
|
}
|
ExternalToolsResult testResult = loadExternalToolsWithTransport(mount);
|
List<AiMcpToolDescriptor> tools = testResult.tools;
|
payload.put("success", true);
|
payload.put("toolCount", tools.size());
|
payload.put("tools", tools);
|
payload.put("resolvedTransportType", testResult.transportType);
|
payload.put("recommendedTransportType", testResult.transportType);
|
payload.put("message", buildExternalSuccessMessage(testResult.transportType, tools.size()));
|
updateTestState(mount, 1, String.valueOf(payload.get("message")), tools.size());
|
return payload;
|
}
|
|
/**
|
* 以“预览”模式执行一次工具调用,便于后台页面调试工具返回内容。
|
*/
|
public AiDiagnosticToolResult previewTool(Long tenantId, String mountCode, String toolCode, String sceneCode, String question) {
|
AiMcpMount mount = aiMcpMountService.getTenantMountByCode(tenantId, mountCode);
|
if (mount == null) {
|
throw new IllegalArgumentException("MCP挂载不存在");
|
}
|
AiPromptContext context = new AiPromptContext()
|
.setTenantId(tenantId)
|
.setSceneCode(sceneCode == null || sceneCode.trim().isEmpty() ? AiSceneCode.SYSTEM_DIAGNOSE : sceneCode)
|
.setQuestion(question == null || question.trim().isEmpty() ? "请执行一次MCP工具预览" : question);
|
if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(mount.getTransportType())
|
|| AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(mount.getTransportType())) {
|
return executeExternalTool(mount, toolCode, context, buildToolArguments(context, null));
|
}
|
return executeInternalTool(mountCode, toolCode, context);
|
}
|
|
/**
|
* 根据工具描述符执行一次工具调用。
|
*/
|
public AiDiagnosticToolResult executeTool(Long tenantId, AiMcpToolDescriptor descriptor, AiPromptContext context) {
|
if (descriptor == null) {
|
throw new IllegalArgumentException("MCP工具不存在");
|
}
|
if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(descriptor.getTransportType())
|
|| AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(descriptor.getTransportType())) {
|
AiMcpMount mount = aiMcpMountService.getTenantMountByCode(tenantId, descriptor.getMountCode());
|
if (mount == null) {
|
throw new IllegalArgumentException("MCP挂载不存在");
|
}
|
return executeExternalTool(mount, descriptor.getToolCode(), context, buildToolArguments(context, descriptor));
|
}
|
return executeInternalTool(descriptor.getMountCode(), descriptor.getToolCode(), context);
|
}
|
|
/**
|
* 根据完整 MCP 工具名执行一次工具调用,通常供协议层直接使用。
|
*/
|
public AiDiagnosticToolResult executeTool(Long tenantId, String mcpToolName, AiPromptContext context, Map<String, Object> arguments) {
|
AiMcpToolDescriptor descriptor = findDescriptor(tenantId, mcpToolName);
|
if (descriptor == null) {
|
throw new IllegalArgumentException("MCP工具不存在");
|
}
|
if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(descriptor.getTransportType())
|
|| AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(descriptor.getTransportType())) {
|
AiMcpMount mount = aiMcpMountService.getTenantMountByCode(tenantId, descriptor.getMountCode());
|
if (mount == null) {
|
throw new IllegalArgumentException("MCP挂载不存在");
|
}
|
return executeExternalTool(mount, descriptor.getToolCode(), context, arguments);
|
}
|
return executeInternalTool(descriptor.getMountCode(), descriptor.getToolCode(), context);
|
}
|
|
/**
|
* 确保租户存在默认本地 MCP 挂载。
|
*/
|
public void ensureDefaultMount(Long tenantId, Long userId) {
|
if (aiMcpMountService.getTenantMountByCode(tenantId, AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE) != null) {
|
return;
|
}
|
Date now = new Date();
|
AiMcpMount mount = new AiMcpMount()
|
.setUuid(String.valueOf(System.currentTimeMillis()))
|
.setName(AiMcpConstants.DEFAULT_LOCAL_MOUNT_NAME)
|
.setMountCode(AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE)
|
.setTransportType(AiMcpConstants.TRANSPORT_INTERNAL)
|
.setUrl("/ai/mcp")
|
.setEnabledFlag(1)
|
.setTimeoutMs(10000)
|
.setStatus(1)
|
.setDeleted(0)
|
.setTenantId(tenantId)
|
.setCreateBy(userId)
|
.setCreateTime(now)
|
.setUpdateBy(userId)
|
.setUpdateTime(now)
|
.setMemo("默认挂载当前 WMS AI 内置工具集合");
|
aiMcpMountService.save(mount);
|
}
|
|
/**
|
* 为内部工具结果补齐挂载编码和标准 MCP 工具名。
|
*/
|
public AiDiagnosticToolResult decorateResult(AiDiagnosticToolResult result) {
|
if (result == null) {
|
return null;
|
}
|
return decorateResult(result, AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE, findProvider(result.getToolCode()));
|
}
|
|
private AiDiagnosticToolResult decorateResult(AiDiagnosticToolResult result, String mountCode, AiDiagnosticDataProvider provider) {
|
String actualMountCode = mountCode == null || mountCode.trim().isEmpty() ? AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE : mountCode;
|
result.setMountCode(actualMountCode);
|
result.setMcpToolName(aiMcpPayloadMapper.buildMcpToolName(actualMountCode, result.getToolCode()));
|
if ((result.getToolName() == null || result.getToolName().trim().isEmpty()) && provider != null) {
|
result.setToolName(provider.getToolName());
|
}
|
return result;
|
}
|
|
/**
|
* 按当前租户的工具配置生成内部 MCP 工具目录。
|
*/
|
private List<AiMcpToolDescriptor> buildInternalTools(Long tenantId, AiMcpMount mount) {
|
Map<String, AiDiagnosticToolConfig> configMap = buildInternalConfigMap(tenantId);
|
List<AiDiagnosticDataProvider> sortedProviders = new ArrayList<>(providers);
|
sortedProviders.sort(Comparator.comparingInt(AiDiagnosticDataProvider::getOrder));
|
List<AiMcpToolDescriptor> output = new ArrayList<>();
|
for (AiDiagnosticDataProvider provider : sortedProviders) {
|
AiDiagnosticToolConfig config = configMap.get(provider.getToolCode());
|
String usageScope = aiMcpPayloadMapper.resolveUsageScope(
|
config == null ? null : config.getSceneCode(),
|
config == null ? 1 : config.getEnabledFlag(),
|
config == null ? null : config.getUsageScope()
|
);
|
output.add(new AiMcpToolDescriptor()
|
.setMountCode(mount.getMountCode())
|
.setMountName(mount.getName())
|
.setToolCode(provider.getToolCode())
|
.setMcpToolName(aiMcpPayloadMapper.buildMcpToolName(mount.getMountCode(), provider.getToolCode()))
|
.setToolName(provider.getToolName())
|
.setSceneCode(aiMcpPayloadMapper.resolveSceneCode(usageScope))
|
.setDescription(provider.getDefaultToolPrompt())
|
.setEnabledFlag(AiMcpConstants.USAGE_SCOPE_DISABLED.equals(usageScope) ? 0 : (config == null ? 1 : config.getEnabledFlag()))
|
.setPriority(config == null ? provider.getOrder() : config.getPriority())
|
.setToolPrompt(config == null ? provider.getDefaultToolPrompt() : config.getToolPrompt())
|
.setUsageScope(usageScope)
|
.setTransportType(mount.getTransportType())
|
.setInputSchema(aiMcpPayloadMapper.defaultInputSchema(true)));
|
}
|
return output;
|
}
|
|
/**
|
* 为内置工具目录选择一条最合适的有效配置。
|
* 目录层只有一条工具描述,因此这里优先保留启用且状态正常的配置,
|
* 并优先选择“聊天与诊断都可用”的配置,再退回到“仅诊断”配置。
|
*/
|
private Map<String, AiDiagnosticToolConfig> buildInternalConfigMap(Long tenantId) {
|
Map<String, AiDiagnosticToolConfig> configMap = new LinkedHashMap<>();
|
for (AiDiagnosticToolConfig item : aiDiagnosticToolConfigService.listTenantConfigs(tenantId)) {
|
if (item == null || !Integer.valueOf(1).equals(item.getStatus())) {
|
continue;
|
}
|
AiDiagnosticToolConfig existed = configMap.get(item.getToolCode());
|
if (existed == null || preferInternalConfig(item, existed)) {
|
configMap.put(item.getToolCode(), item);
|
}
|
}
|
return configMap;
|
}
|
|
/**
|
* 内置工具目录优先展示用途更广的配置,其次比较优先级。
|
*/
|
private boolean preferInternalConfig(AiDiagnosticToolConfig candidate, AiDiagnosticToolConfig current) {
|
String candidateUsageScope = aiMcpPayloadMapper.resolveUsageScope(
|
candidate.getSceneCode(),
|
candidate.getEnabledFlag(),
|
candidate.getUsageScope()
|
);
|
String currentUsageScope = aiMcpPayloadMapper.resolveUsageScope(
|
current.getSceneCode(),
|
current.getEnabledFlag(),
|
current.getUsageScope()
|
);
|
int candidateRank = usageScopeRank(candidateUsageScope);
|
int currentRank = usageScopeRank(currentUsageScope);
|
if (candidateRank != currentRank) {
|
return candidateRank < currentRank;
|
}
|
Integer candidatePriority = candidate.getPriority() == null ? Integer.MAX_VALUE : candidate.getPriority();
|
Integer currentPriority = current.getPriority() == null ? Integer.MAX_VALUE : current.getPriority();
|
return candidatePriority < currentPriority;
|
}
|
|
private int usageScopeRank(String usageScope) {
|
if (AiMcpConstants.USAGE_SCOPE_CHAT_AND_DIAGNOSE.equals(usageScope)) {
|
return 0;
|
}
|
if (AiMcpConstants.USAGE_SCOPE_DIAGNOSE_ONLY.equals(usageScope)) {
|
return 1;
|
}
|
return 2;
|
}
|
|
/**
|
* 按工具编码定位内部工具实现。
|
*/
|
private AiDiagnosticDataProvider findProvider(String toolCode) {
|
for (AiDiagnosticDataProvider provider : providers) {
|
if (provider.getToolCode().equals(toolCode)) {
|
return provider;
|
}
|
}
|
return null;
|
}
|
|
/**
|
* 更新挂载测试结果和工具数量,并清理缓存。
|
*/
|
private void updateTestState(AiMcpMount mount, Integer result, String message, Integer toolCount) {
|
mount.setLastTestResult(result);
|
mount.setLastTestMessage(message);
|
mount.setLastToolCount(toolCount);
|
mount.setLastTestTime(new Date());
|
mount.setUpdateTime(new Date());
|
aiMcpMountService.updateById(mount);
|
if (mount.getId() != null) {
|
externalToolCache.remove(buildCacheKey(mount.getTenantId(), mount));
|
}
|
}
|
|
/**
|
* 执行内部 MCP 工具。
|
*/
|
private AiDiagnosticToolResult executeInternalTool(String mountCode, String toolCode, AiPromptContext context) {
|
AiDiagnosticDataProvider provider = findProvider(toolCode);
|
if (provider == null) {
|
throw new IllegalArgumentException("MCP工具不存在");
|
}
|
AiDiagnosticToolResult result = provider.buildDiagnosticData(context);
|
if (result == null) {
|
return new AiDiagnosticToolResult()
|
.setToolCode(toolCode)
|
.setMountCode(mountCode)
|
.setMcpToolName(aiMcpPayloadMapper.buildMcpToolName(mountCode, toolCode))
|
.setToolName(provider.getToolName())
|
.setSeverity("WARN")
|
.setSummaryText("工具没有返回结果");
|
}
|
return decorateResult(result, mountCode, provider);
|
}
|
|
/**
|
* 执行外部 MCP 工具,并补齐本地系统所需的统一字段。
|
*/
|
private AiDiagnosticToolResult executeExternalTool(AiMcpMount mount, String toolCode,
|
AiPromptContext context, Map<String, Object> arguments) {
|
ExternalToolCallResult callResult = callExternalTool(mount, toolCode, arguments);
|
AiDiagnosticToolResult result = callResult.result;
|
return result
|
.setToolCode(toolCode)
|
.setMountCode(mount.getMountCode())
|
.setMcpToolName(aiMcpPayloadMapper.buildMcpToolName(mount.getMountCode(), toolCode))
|
.setToolName(result.getToolName() == null || result.getToolName().trim().isEmpty() ? toolCode : result.getToolName())
|
.setRawMeta(result.getRawMeta() == null ? new LinkedHashMap<String, Object>() : result.getRawMeta());
|
}
|
|
/**
|
* 真正加载外部工具目录,不带缓存。
|
*/
|
private List<AiMcpToolDescriptor> loadExternalTools(AiMcpMount mount) {
|
return loadExternalToolsWithTransport(mount).tools;
|
}
|
|
/**
|
* 带短期缓存地加载外部工具目录,避免同一挂载在短时间内重复握手。
|
*/
|
private List<AiMcpToolDescriptor> loadCachedExternalTools(Long tenantId, AiMcpMount mount) {
|
String cacheKey = buildCacheKey(tenantId, mount);
|
CachedTools cached = externalToolCache.get(cacheKey);
|
long now = System.currentTimeMillis();
|
if (cached != null && cached.expireAt > now) {
|
return new ArrayList<>(cached.tools);
|
}
|
List<AiMcpToolDescriptor> tools = loadExternalTools(mount);
|
externalToolCache.put(cacheKey, new CachedTools(new ArrayList<>(tools), now + EXTERNAL_TOOL_CACHE_TTL_MS));
|
return tools;
|
}
|
|
/**
|
* 生成外部工具目录缓存 key。
|
*/
|
private String buildCacheKey(Long tenantId, AiMcpMount mount) {
|
return String.valueOf(tenantId) + ":" + mount.getId() + ":" + (mount.getUpdateTime() == null ? 0L : mount.getUpdateTime().getTime());
|
}
|
|
/**
|
* 按完整 MCP 工具名反查工具描述符。
|
*/
|
private AiMcpToolDescriptor findDescriptor(Long tenantId, String mcpToolName) {
|
for (AiMcpToolDescriptor descriptor : listTools(tenantId, null)) {
|
if (descriptor != null && mcpToolName.equals(descriptor.getMcpToolName())) {
|
return descriptor;
|
}
|
}
|
return null;
|
}
|
|
/**
|
* 根据上下文和工具信息构造远程调用参数。
|
*/
|
private Map<String, Object> buildToolArguments(AiPromptContext context, AiMcpToolDescriptor descriptor) {
|
Map<String, Object> arguments = new LinkedHashMap<>();
|
if (context != null) {
|
arguments.put("tenantId", context.getTenantId());
|
arguments.put("sceneCode", context.getSceneCode());
|
arguments.put("question", context.getQuestion());
|
arguments.put("sessionId", context.getSessionId());
|
}
|
if (descriptor != null) {
|
arguments.put("toolName", descriptor.getToolName());
|
arguments.put("mountCode", descriptor.getMountCode());
|
}
|
return arguments;
|
}
|
|
/**
|
* 生成外部挂载测试成功文案。
|
*/
|
private String buildExternalSuccessMessage(String transportType, int toolCount) {
|
String prefix = AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(transportType)
|
? "远程SSE MCP工具加载成功"
|
: "远程Streamable HTTP MCP工具加载成功";
|
return prefix + ",发现 " + toolCount + " 个工具";
|
}
|
|
/**
|
* 根据挂载配置选择 HTTP / SSE 客户端去加载外部工具目录。
|
* AUTO 模式会依次尝试 Streamable HTTP 和 SSE。
|
*/
|
private ExternalToolsResult loadExternalToolsWithTransport(AiMcpMount mount) {
|
if (AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(mount.getTransportType())) {
|
return new ExternalToolsResult(AiMcpConstants.TRANSPORT_SSE, aiMcpSseClient.listTools(mount));
|
}
|
if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(mount.getTransportType())) {
|
return new ExternalToolsResult(AiMcpConstants.TRANSPORT_HTTP, aiMcpHttpClient.listTools(mount));
|
}
|
List<String> errors = new ArrayList<>();
|
try {
|
return new ExternalToolsResult(AiMcpConstants.TRANSPORT_HTTP, aiMcpHttpClient.listTools(copyMountWithTransport(mount, AiMcpConstants.TRANSPORT_HTTP)));
|
} catch (Exception e) {
|
errors.add("HTTP: " + e.getMessage());
|
}
|
try {
|
return new ExternalToolsResult(AiMcpConstants.TRANSPORT_SSE, aiMcpSseClient.listTools(copyMountWithTransport(mount, AiMcpConstants.TRANSPORT_SSE)));
|
} catch (Exception e) {
|
errors.add("SSE: " + e.getMessage());
|
}
|
throw new IllegalStateException(String.join(";", errors));
|
}
|
|
/**
|
* 根据挂载配置选择 HTTP / SSE 客户端执行远程工具。
|
*/
|
private ExternalToolCallResult callExternalTool(AiMcpMount mount, String toolCode, Map<String, Object> arguments) {
|
if (AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(mount.getTransportType())) {
|
return new ExternalToolCallResult(AiMcpConstants.TRANSPORT_SSE, aiMcpSseClient.callTool(mount, toolCode, arguments));
|
}
|
if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(mount.getTransportType())) {
|
return new ExternalToolCallResult(AiMcpConstants.TRANSPORT_HTTP, aiMcpHttpClient.callTool(mount, toolCode, arguments));
|
}
|
List<String> errors = new ArrayList<>();
|
try {
|
return new ExternalToolCallResult(
|
AiMcpConstants.TRANSPORT_HTTP,
|
aiMcpHttpClient.callTool(copyMountWithTransport(mount, AiMcpConstants.TRANSPORT_HTTP), toolCode, arguments)
|
);
|
} catch (Exception e) {
|
errors.add("HTTP: " + e.getMessage());
|
}
|
try {
|
return new ExternalToolCallResult(
|
AiMcpConstants.TRANSPORT_SSE,
|
aiMcpSseClient.callTool(copyMountWithTransport(mount, AiMcpConstants.TRANSPORT_SSE), toolCode, arguments)
|
);
|
} catch (Exception e) {
|
errors.add("SSE: " + e.getMessage());
|
}
|
throw new IllegalStateException(String.join(";", errors));
|
}
|
|
/**
|
* 复制一份挂载对象,并覆盖指定传输协议,供 AUTO 探测流程使用。
|
*/
|
private AiMcpMount copyMountWithTransport(AiMcpMount mount, String transportType) {
|
return new AiMcpMount()
|
.setId(mount.getId())
|
.setUuid(mount.getUuid())
|
.setName(mount.getName())
|
.setMountCode(mount.getMountCode())
|
.setTransportType(transportType)
|
.setUrl(mount.getUrl())
|
.setAuthType(mount.getAuthType())
|
.setAuthValue(mount.getAuthValue())
|
.setUsageScope(mount.getUsageScope())
|
.setEnabledFlag(mount.getEnabledFlag())
|
.setTimeoutMs(mount.getTimeoutMs())
|
.setStatus(mount.getStatus())
|
.setTenantId(mount.getTenantId())
|
.setCreateBy(mount.getCreateBy())
|
.setCreateTime(mount.getCreateTime())
|
.setUpdateBy(mount.getUpdateBy())
|
.setUpdateTime(mount.getUpdateTime())
|
.setMemo(mount.getMemo());
|
}
|
|
private static class CachedTools {
|
private final List<AiMcpToolDescriptor> tools;
|
private final long expireAt;
|
|
/**
|
* 保存一次外部工具目录缓存内容及其失效时间。
|
*/
|
private CachedTools(List<AiMcpToolDescriptor> tools, long expireAt) {
|
this.tools = tools;
|
this.expireAt = expireAt;
|
}
|
}
|
|
private static class ExternalToolsResult {
|
private final String transportType;
|
private final List<AiMcpToolDescriptor> tools;
|
|
/**
|
* 保存一次外部工具目录加载结果及实际命中的传输协议。
|
*/
|
private ExternalToolsResult(String transportType, List<AiMcpToolDescriptor> tools) {
|
this.transportType = transportType;
|
this.tools = tools;
|
}
|
}
|
|
private static class ExternalToolCallResult {
|
private final String transportType;
|
private final AiDiagnosticToolResult result;
|
|
/**
|
* 保存一次外部工具调用结果及实际命中的传输协议。
|
*/
|
private ExternalToolCallResult(String transportType, AiDiagnosticToolResult result) {
|
this.transportType = transportType;
|
this.result = result;
|
}
|
}
|
}
|