| | |
| | | private final ObjectMapper objectMapper; |
| | | |
| | | @Override |
| | | public List<AiMcpMount> listActiveMounts() { |
| | | public List<AiMcpMount> listActiveMounts(Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | return this.list(new LambdaQueryWrapper<AiMcpMount>() |
| | | .eq(AiMcpMount::getTenantId, tenantId) |
| | | .eq(AiMcpMount::getStatus, StatusType.ENABLE.val) |
| | | .eq(AiMcpMount::getDeleted, 0) |
| | | .orderByAsc(AiMcpMount::getSort) |
| | | .orderByAsc(AiMcpMount::getId)); |
| | | } |
| | | |
| | | @Override |
| | | public void validateBeforeSave(AiMcpMount aiMcpMount) { |
| | | public void validateBeforeSave(AiMcpMount aiMcpMount, Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | aiMcpMount.setTenantId(tenantId); |
| | | fillDefaults(aiMcpMount); |
| | | ensureRequiredFields(aiMcpMount); |
| | | ensureRequiredFields(aiMcpMount, tenantId); |
| | | } |
| | | |
| | | @Override |
| | | public void validateBeforeUpdate(AiMcpMount aiMcpMount) { |
| | | public void validateBeforeUpdate(AiMcpMount aiMcpMount, Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | fillDefaults(aiMcpMount); |
| | | if (aiMcpMount.getId() == null) { |
| | | throw new CoolException("MCP 挂载 ID 不能为空"); |
| | | } |
| | | ensureRequiredFields(aiMcpMount); |
| | | AiMcpMount current = requireMount(aiMcpMount.getId(), tenantId); |
| | | aiMcpMount.setTenantId(current.getTenantId()); |
| | | ensureRequiredFields(aiMcpMount, tenantId); |
| | | } |
| | | |
| | | @Override |
| | | public List<AiMcpToolPreviewDto> previewTools(Long mountId, Long userId, Long tenantId) { |
| | | AiMcpMount mount = requireMount(mountId); |
| | | AiMcpMount mount = requireMount(mountId, tenantId); |
| | | try (McpMountRuntimeFactory.McpMountRuntime runtime = mcpMountRuntimeFactory.create(List.of(mount), userId)) { |
| | | List<AiMcpToolPreviewDto> tools = new ArrayList<>(); |
| | | for (ToolCallback callback : runtime.getToolCallbacks()) { |
| | |
| | | } catch (Exception e) { |
| | | throw new CoolException("工具输入 JSON 格式错误: " + e.getMessage()); |
| | | } |
| | | AiMcpMount mount = requireMount(mountId); |
| | | AiMcpMount mount = requireMount(mountId, tenantId); |
| | | try (McpMountRuntimeFactory.McpMountRuntime runtime = mcpMountRuntimeFactory.create(List.of(mount), userId)) { |
| | | ToolCallback callback = Arrays.stream(runtime.getToolCallbacks()) |
| | | .filter(item -> item != null && item.getToolDefinition() != null) |
| | |
| | | } |
| | | } |
| | | |
| | | private void ensureRequiredFields(AiMcpMount aiMcpMount) { |
| | | private void ensureRequiredFields(AiMcpMount aiMcpMount, Long tenantId) { |
| | | if (!StringUtils.hasText(aiMcpMount.getName())) { |
| | | throw new CoolException("MCP 挂载名称不能为空"); |
| | | } |
| | | if (AiDefaults.MCP_TRANSPORT_BUILTIN.equals(aiMcpMount.getTransportType())) { |
| | | builtinMcpToolRegistry.validateBuiltinCode(aiMcpMount.getBuiltinCode()); |
| | | ensureBuiltinConflictFree(aiMcpMount); |
| | | ensureBuiltinConflictFree(aiMcpMount, tenantId); |
| | | return; |
| | | } |
| | | if (AiDefaults.MCP_TRANSPORT_SSE_HTTP.equals(aiMcpMount.getTransportType())) { |
| | |
| | | throw new CoolException("不支持的 MCP 传输类型: " + aiMcpMount.getTransportType()); |
| | | } |
| | | |
| | | private AiMcpMount requireMount(Long mountId) { |
| | | private AiMcpMount requireMount(Long mountId, Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | if (mountId == null) { |
| | | throw new CoolException("MCP 挂载 ID 不能为空"); |
| | | } |
| | | AiMcpMount mount = this.getById(mountId); |
| | | if (mount == null || (mount.getDeleted() != null && mount.getDeleted() == 1)) { |
| | | AiMcpMount mount = this.getOne(new LambdaQueryWrapper<AiMcpMount>() |
| | | .eq(AiMcpMount::getId, mountId) |
| | | .eq(AiMcpMount::getTenantId, tenantId) |
| | | .eq(AiMcpMount::getDeleted, 0) |
| | | .last("limit 1")); |
| | | if (mount == null) { |
| | | throw new CoolException("MCP 挂载不存在"); |
| | | } |
| | | return mount; |
| | | } |
| | | |
| | | private void ensureBuiltinConflictFree(AiMcpMount aiMcpMount) { |
| | | private void ensureBuiltinConflictFree(AiMcpMount aiMcpMount, Long tenantId) { |
| | | if (aiMcpMount.getStatus() == null || aiMcpMount.getStatus() != StatusType.ENABLE.val) { |
| | | return; |
| | | } |
| | |
| | | return; |
| | | } |
| | | LambdaQueryWrapper<AiMcpMount> queryWrapper = new LambdaQueryWrapper<AiMcpMount>() |
| | | .eq(AiMcpMount::getTenantId, tenantId) |
| | | .eq(AiMcpMount::getTransportType, AiDefaults.MCP_TRANSPORT_BUILTIN) |
| | | .eq(AiMcpMount::getStatus, StatusType.ENABLE.val) |
| | | .eq(AiMcpMount::getDeleted, 0) |
| | | .in(AiMcpMount::getBuiltinCode, conflictCodes); |
| | | if (aiMcpMount.getId() != null) { |
| | | queryWrapper.ne(AiMcpMount::getId, aiMcpMount.getId()); |
| | |
| | | } |
| | | return codes; |
| | | } |
| | | |
| | | private void ensureTenantId(Long tenantId) { |
| | | if (tenantId == null) { |
| | | throw new CoolException("当前租户不存在"); |
| | | } |
| | | } |
| | | } |