From e9a22c764a4d74e46576f055486ebf734aa2ab49 Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Mon, 6 Oct 2025 17:39:35 +0530 Subject: [PATCH 1/3] MCP Recon code reusability added for tool, resource discovery, issue fixed and capability added --- .../akto/hybrid_parsers/HttpCallParser.java | 21 +- .../java/com/akto/hybrid_runtime/Main.java | 10 +- .../akto/hybrid_runtime/McpReconScanner.java | 73 ++--- .../McpReconSyncJobExecutor.java | 109 ++++---- .../McpToolsSyncJobExecutor.java | 255 +++++++++++------- 5 files changed, 241 insertions(+), 227 deletions(-) diff --git a/apps/mini-runtime/src/main/java/com/akto/hybrid_parsers/HttpCallParser.java b/apps/mini-runtime/src/main/java/com/akto/hybrid_parsers/HttpCallParser.java index be67c4f7e2..239ee2b20d 100644 --- a/apps/mini-runtime/src/main/java/com/akto/hybrid_parsers/HttpCallParser.java +++ b/apps/mini-runtime/src/main/java/com/akto/hybrid_parsers/HttpCallParser.java @@ -362,13 +362,13 @@ public static Pair applyAdvancedFilters(HttpResp return new Pair(responseParams, FILTER_TYPE.ERROR); } - public void syncFunction(List responseParams, boolean syncImmediately, boolean fetchAllSTI, AccountSettings accountSettings) { + public void syncFunction(List responseParams, boolean syncImmediately, boolean fetchAllSTI, AccountSettings accountSettings, boolean isRecon) { // USE ONLY filteredResponseParams and not responseParams List filteredResponseParams = responseParams; if (accountSettings != null && accountSettings.getDefaultPayloads() != null) { filteredResponseParams = filterDefaultPayloads(filteredResponseParams, accountSettings.getDefaultPayloads()); } - filteredResponseParams = filterHttpResponseParams(filteredResponseParams, accountSettings); + filteredResponseParams = filterHttpResponseParams(filteredResponseParams, accountSettings, isRecon); boolean isHarOrPcap = aggregate(filteredResponseParams, aggregatorMap); apiCatalogSync.setMergeUrlsOnVersions(accountSettings.isAllowMergingOnVersions()); @@ -508,13 +508,16 @@ private static void sendTrafficMetricsToTelemetry(BasicDBObject metricsData) { } } - public static boolean useHostCondition(String hostName, HttpResponseParams.Source source) { + public static boolean useHostCondition(String hostName, HttpResponseParams.Source source, boolean isRecon) { List whiteListSource = Arrays.asList(HttpResponseParams.Source.MIRRORING); boolean hostNameCondition; if (hostName == null) { hostNameCondition = false; - } else { - hostNameCondition = ! ( hostName.toLowerCase().equals(hostName.toUpperCase()) ); + } else if (isRecon) { + hostNameCondition = true; + } + else { + hostNameCondition = ! ( hostName.toLowerCase().equals(hostName.toUpperCase()) ); } return whiteListSource.contains(source) && hostNameCondition && ApiCollection.useHost; } @@ -644,7 +647,7 @@ public void updateApiCollectionTags(String hostNameMapKey, HttpResponseParams ht } } - public int createApiCollectionId(HttpResponseParams httpResponseParam){ + public int createApiCollectionId(HttpResponseParams httpResponseParam, boolean isRecon){ int apiCollectionId; String hostName = getHeaderValue(httpResponseParam.getRequestParams().getHeaders(), "host"); @@ -656,7 +659,7 @@ public int createApiCollectionId(HttpResponseParams httpResponseParam){ String vpcId = System.getenv("VPC_ID"); List tagList = CollectionTags.convertTagsFormat(httpResponseParam.getTags()); - if (useHostCondition(hostName, httpResponseParam.getSource())) { + if (useHostCondition(hostName, httpResponseParam.getSource(), isRecon)) { hostName = hostName.toLowerCase(); hostName = hostName.trim(); @@ -748,7 +751,7 @@ private boolean isBlankResponseBodyForGET(String method, String contentType, Str return res; } - public List filterHttpResponseParams(List httpResponseParamsList, AccountSettings accountSettings) { + public List filterHttpResponseParams(List httpResponseParamsList, AccountSettings accountSettings, boolean isRecon) { List filteredResponseParams = new ArrayList<>(); int originalSize = httpResponseParamsList.size(); @@ -865,7 +868,7 @@ public List filterHttpResponseParams(List { @@ -539,7 +539,7 @@ private static void kafkaSubscribeAndProcess(String topicName, boolean syncImmed apiConfig, fetchAllSTI, syncImmediately, - centralKafkaTopicName); + centralKafkaTopicName, false); AllMetrics.instance.setRuntimeProcessLatency(System.currentTimeMillis()-start); loggerMaker.info("Processed " + responseParamsToAccountMap.size() + " accounts in " + (System.currentTimeMillis()-start) + " ms"); } @@ -645,7 +645,7 @@ public static void runDBMaintenanceJob(APIConfig apiConfig) { public static void handleResponseParams(Map> responseParamsToAccountMap, Map accountInfoMap, boolean isDashboardInstance, Map httpCallParserMap, APIConfig apiConfig, boolean fetchAllSTI, - boolean syncImmediately, String centralKafkaTopicName) { + boolean syncImmediately, String centralKafkaTopicName, boolean isRecon) { for (String accountId: responseParamsToAccountMap.keySet()) { int accountIdInt; try { @@ -690,7 +690,7 @@ public static void handleResponseParams(Map> re accWiseResponse = filterBasedOnHeaders(accWiseResponse, accountInfo.accountSettings); loggerMaker.infoAndAddToDb("Initiating sync function for account: " + accountId); - parser.syncFunction(accWiseResponse, syncImmediately, fetchAllSTI, accountInfo.accountSettings); + parser.syncFunction(accWiseResponse, syncImmediately, fetchAllSTI, accountInfo.accountSettings, isRecon); loggerMaker.debugInfoAddToDb("Sync function completed for account: " + accountId); sendToCentralKafka(centralKafkaTopicName, accWiseResponse); @@ -1047,7 +1047,7 @@ public static void processData(List data) { apiConfig, fetchAllSTI, syncImmediately, - centralKafkaTopicName); + centralKafkaTopicName, false); AllMetrics.instance.setRuntimeProcessLatency(System.currentTimeMillis()-start); } diff --git a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconScanner.java b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconScanner.java index 051c8cbee4..93f65893a1 100644 --- a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconScanner.java +++ b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconScanner.java @@ -340,59 +340,26 @@ private void cleanFailedIpCache() { * Optimized MCP verification using CompletableFuture with timeout management */ private List verifyMcpBatch(List openTargets) { - // Use CompletableFuture for better control - List> futures = new ArrayList<>(openTargets.size()); + logger.info(String.format("Starting MCP verification for %d targets", openTargets.size())); + List results = new ArrayList<>(); for (IpPortPair target : openTargets) { - CompletableFuture future = CompletableFuture - .supplyAsync(() -> { - try { - semaphore.acquire(); - try { - return verifySingleMcp(target.ip, target.port); - } finally { - semaphore.release(); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return null; - } - }, executorService); - - // Add timeout using Java 8 compatible approach - final CompletableFuture timeoutFuture = new CompletableFuture<>(); - executorService.submit(() -> { - try { - Thread.sleep(timeout * 2); - timeoutFuture.complete(null); - } catch (InterruptedException e) { - // Ignore + try { + logger.debug(String.format("Checking MCP at %s:%d", target.ip, target.port)); + McpServer result = verifySingleMcp(target.ip, target.port); + if (result != null) { + logger.info(String.format("MCP server found at %s:%d", target.ip, target.port)); + results.add(result); + } else { + logger.debug(String.format("No MCP server at %s:%d", target.ip, target.port)); } - }); - - CompletableFuture resultFuture = future - .applyToEither(timeoutFuture, result -> result) - .exceptionally(ex -> { - logger.debug("Verification timeout for " + target.ip + ":" + target.port); - return null; - }); - - futures.add(resultFuture); - } - - // Wait for all completions or timeout - try { - CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) - .get(timeout * 3, TimeUnit.MILLISECONDS); - } catch (Exception e) { - // Continue with completed futures + } catch (Exception e) { + logger.debug("Verification error for " + target.ip + ":" + target.port + ": " + e.getMessage()); + } } - // Collect non-null results - return futures.stream() - .map(future -> future.getNow(null)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + logger.info(String.format("MCP verification completed. Found %d servers out of %d targets", results.size(), openTargets.size())); + return results; } /** @@ -443,9 +410,6 @@ private McpServer checkSseEndpoint(String baseUrl, String ip, int port) { if (response.getStatusCode() == 200) { String contentType = response.getHeaders().getOrDefault("Content-Type", Collections.singletonList("")).get(0); if (contentType != null && contentType.contains("text/event-stream")) { - String content = response.getBody() != null ? response.getBody().toLowerCase() : ""; - for (String indicator : McpConstants.MCP_INDICATORS) { - if (content.contains(indicator.toLowerCase())) { McpServer server = new McpServer(); server.setIp(ip); server.setPort(port); @@ -456,8 +420,6 @@ private McpServer checkSseEndpoint(String baseUrl, String ip, int port) { server.setType("SSE"); server.setEndpoint(endpoint); return server; - } - } } } } catch (Exception e) { @@ -539,8 +501,7 @@ private McpServer checkHttpEndpoints(String baseUrl, String ip, int port) { Map> headers = new HashMap<>(); OriginalHttpRequest request = new OriginalHttpRequest(url, "", "GET", null, headers, ""); OriginalHttpResponse response = ApiExecutor.sendRequest(request, true, null, false, new ArrayList<>()); - String content = response.getBody(); - if (isLikelyMcp(content)) { + if(response.getStatusCode() == 200) { McpServer server = new McpServer(); server.setIp(ip); server.setPort(port); @@ -549,7 +510,7 @@ private McpServer checkHttpEndpoints(String baseUrl, String ip, int port) { server.setDetectionMethod("HTTP"); server.setTimestamp(new Date().toString()); server.setEndpoint(endpoint); - List detectedIndicators = getDetectedIndicators(content); + List detectedIndicators = getDetectedIndicators("HTTP"); Map serverInfo = new HashMap<>(); serverInfo.put("detectedIndicators", detectedIndicators); server.setServerInfo(serverInfo); diff --git a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconSyncJobExecutor.java b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconSyncJobExecutor.java index ae3fff308e..4db652c801 100644 --- a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconSyncJobExecutor.java +++ b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconSyncJobExecutor.java @@ -321,12 +321,24 @@ private void processScanResults(List scanResults, APIConfig apiC // Add to batch serverBatch.add(mcpReconResult); - List toolsResponseList = toolsDiscovery(server); - List resourcesResponseList = resourcesDiscovery(server); + + List toolsResponseList = new ArrayList<>(); + List resourcesResponseList = new ArrayList<>(); + List endpointsResponseList = new ArrayList<>(); + if(server.getTools() != null) { + toolsResponseList = McpToolsSyncJobExecutor.INSTANCE.handleMcpToolsDiscovery(null, new HashSet<>(), true, server); + } + if(server.getResources() != null) { + resourcesResponseList = McpToolsSyncJobExecutor.INSTANCE.handleMcpResourceDiscovery(null, new HashSet<>(), true, server); + } + if(server.getEndpoint() != null && !server.getEndpoint().isEmpty()){ + endpointsResponseList = handleMcpRequestDiscovery(server); + } List responseParamsToProcess = new ArrayList<>(); responseParamsToProcess.addAll(toolsResponseList); responseParamsToProcess.addAll(resourcesResponseList); - McpToolsSyncJobExecutor.processResponseParams(apiConfig, responseParamsToProcess); + responseParamsToProcess.addAll(endpointsResponseList); + McpToolsSyncJobExecutor.processResponseParams(apiConfig, responseParamsToProcess, true); // Insert when batch is full if (serverBatch.size() >= BATCH_SIZE) { @@ -494,73 +506,44 @@ private static class ScanCacheEntry { } } + public List handleMcpRequestDiscovery(McpServer mcpServer) { - private List toolsDiscovery(McpServer mcpServer) { - String host = mcpServer.getUrl(); + String host = mcpServer.getIp() + ":" + mcpServer.getPort(); ObjectMapper mapper = new ObjectMapper(); - List responseParamsList = new ArrayList<>(); - try { - int id = 1; - String toolsCallRequestHeaders = buildHeaders(host); - for (McpSchema.Tool tool : mcpServer.getTools()) { + try { + String requestHeaders = buildHeaders(host); McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest( - McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_TOOLS_CALL, - id++, - new McpSchema.CallToolRequest(tool.getName(), McpToolsSyncJobExecutor.generateExampleArguments(tool.getInputSchema())) - ); - - HttpResponseParams toolsCallHttpResponseParams = McpToolsSyncJobExecutor.convertToAktoFormat(0, - mcpServer.getUrl(), - toolsCallRequestHeaders, - HttpMethod.POST.name(), - mapper.writeValueAsString(request), - new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); - - if (toolsCallHttpResponseParams != null) { - responseParamsList.add(toolsCallHttpResponseParams); - } - } - } catch (Exception e) { - logger.error("Error while discovering mcp tools for hostname: {}", host, e); - } - return responseParamsList; - } - - - private List resourcesDiscovery(McpServer mcpServer) { - String host = mcpServer.getUrl(); - ObjectMapper mapper = new ObjectMapper(); - - List responseParamsList = new ArrayList<>(); - try { - int id = 1; - String resourceCallRequestHeaders = buildHeaders(host); - for (McpSchema.Resource resource : mcpServer.getResources()) { - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest( - McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_RESOURCES_READ, - id++, - new McpSchema.ReadResourceRequest(resource.getUri()) - ); - - HttpResponseParams readResourceHttpResponseParams = McpToolsSyncJobExecutor.convertToAktoFormat(0, - mcpServer.getUrl(), - resourceCallRequestHeaders, - HttpMethod.POST.name(), - mapper.writeValueAsString(request), - new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); + McpSchema.JSONRPC_VERSION, + mcpServer.getEndpoint(), + String.valueOf(1), + new McpSchema.InitializeRequest( + McpSchema.LATEST_PROTOCOL_VERSION, + new McpSchema.ClientCapabilities( + null, + null, + null + ), + new McpSchema.Implementation("akto-api-recon-scan", "1.0.0") + ) + ); + + HttpResponseParams requestHttpResponseParams = McpToolsSyncJobExecutor.convertToAktoFormat(0, + mcpServer.getUrl(), + requestHeaders, + HttpMethod.GET.name(), + mapper.writeValueAsString(request), + new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); + + if (requestHttpResponseParams != null) { + responseParamsList.add(requestHttpResponseParams); + } - if (readResourceHttpResponseParams != null) { - responseParamsList.add(readResourceHttpResponseParams); - } + } catch (Exception e) { + logger.error("Error while discovering mcp resources for hostname: {}", host, e); } - } catch (Exception e) { - logger.error("Error while discovering mcp resources for hostname: {}", host, e); + return responseParamsList; } - return responseParamsList; - } private String buildHeaders(String host) { return "{\"Content-Type\":\"application/json\",\"Accept\":\"*/*\",\"host\":\"" + host + "\"}"; diff --git a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java index 644e8aba50..9a1450fa34 100644 --- a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java +++ b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java @@ -33,6 +33,7 @@ import com.akto.util.Constants; import com.akto.util.JSONUtils; import com.akto.util.Pair; +import com.akto.utils.McpServer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import io.swagger.oas.inflector.examples.ExampleBuilder; @@ -102,14 +103,14 @@ public void runJob(APIConfig apiConfig) { List initResponseList = initializeMcpServerCapabilities(apiCollection, normalizedSampleDataSet); List toolsResponseList = handleMcpToolsDiscovery(apiCollection, - normalizedSampleDataSet); + normalizedSampleDataSet, false, null); List resourcesResponseList = handleMcpResourceDiscovery(apiCollection, - normalizedSampleDataSet); + normalizedSampleDataSet, false, null); List responseParamsToProcess = new ArrayList<>(); responseParamsToProcess.addAll(initResponseList); responseParamsToProcess.addAll(toolsResponseList); responseParamsToProcess.addAll(resourcesResponseList); - processResponseParams(apiConfig, responseParamsToProcess); + processResponseParams(apiConfig, responseParamsToProcess, false); } catch (Exception e) { logger.error("Error while running MCP sync job for apiCollectionId: {} and hostname: {}", apiCollection.getId(), apiCollection.getHostName(), e); @@ -153,126 +154,191 @@ private List initializeMcpServerCapabilities(ApiCollection a return responseParamsList; } - private List handleMcpToolsDiscovery(ApiCollection apiCollection, - Set normalizedSampleDataSet) { - String host = apiCollection.getHostName(); + public List handleMcpToolsDiscovery(ApiCollection apiCollection, + Set normalizedSampleDataSet, boolean isRecon, McpServer mcpServer) { + String host =""; List responseParamsList = new ArrayList<>(); - try { - if (mcpServerCapabilities != null && mcpServerCapabilities.getTools() == null) { - logger.debug("Skipping tools discovery as MCP server capabilities do not support tools."); - return responseParamsList; - } - - Pair toolsListResponsePair = getMcpMethodResponse( - host, McpSchema.METHOD_TOOLS_LIST, MCP_TOOLS_LIST_REQUEST_JSON, apiCollection); + if(isRecon) { + host = mcpServer.getIp() + ":" + mcpServer.getPort(); - if (toolsListResponsePair.getSecond() != null && !normalizedSampleDataSet.contains( - McpSchema.METHOD_TOOLS_LIST)) { - responseParamsList.add(toolsListResponsePair.getSecond()); - } - logger.debug("Received tools/list response. Processing tools....."); - - ListToolsResult toolsResult = JSONUtils.fromJson(toolsListResponsePair.getFirst().getResult(), - ListToolsResult.class); - - if (toolsResult != null && !CollectionUtils.isEmpty(toolsResult.getTools())) { - int id = 2; - String urlWithQueryParams = toolsListResponsePair.getSecond().getRequestParams().getURL(); + try { + int id = 1; String toolsCallRequestHeaders = buildHeaders(host); - - for (Tool tool : toolsResult.getTools()) { - if (normalizedSampleDataSet.contains(McpSchema.METHOD_TOOLS_CALL + "/" + tool.getName())) { - logger.debug("Skipping tool {} as it is already present in the db.", tool.getName()); - continue; - } - JSONRPCRequest request = new JSONRPCRequest( - McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_TOOLS_CALL, - id++, - new CallToolRequest(tool.getName(), generateExampleArguments(tool.getInputSchema())) + for (McpSchema.Tool tool : mcpServer.getTools()) { + McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest( + McpSchema.JSONRPC_VERSION, + McpSchema.METHOD_TOOLS_CALL, + id++, + new McpSchema.CallToolRequest(tool.getName(), McpToolsSyncJobExecutor.generateExampleArguments(tool.getInputSchema())) ); - HttpResponseParams toolsCallHttpResponseParams = convertToAktoFormat(apiCollection.getId(), - urlWithQueryParams, - toolsCallRequestHeaders, - HttpMethod.POST.name(), - mapper.writeValueAsString(request), - new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); + HttpResponseParams toolsCallHttpResponseParams = McpToolsSyncJobExecutor.convertToAktoFormat(0, + mcpServer.getUrl(), + toolsCallRequestHeaders, + HttpMethod.POST.name(), + mapper.writeValueAsString(request), + new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); if (toolsCallHttpResponseParams != null) { responseParamsList.add(toolsCallHttpResponseParams); } } - } else { - logger.debug("Skipping as List Tools Result is null or Tools are empty"); + } catch (Exception e) { + logger.error("Error while discovering mcp tools for hostname: {}", host, e); } - } catch (Exception e) { - logger.error("Error while discovering mcp tools for hostname: {}", host, e); + return responseParamsList; } - return responseParamsList; - } - - private List handleMcpResourceDiscovery(ApiCollection apiCollection, - Set normalizedSampleDataSet) { - String host = apiCollection.getHostName(); + else { + host = apiCollection.getHostName(); + try { + if (mcpServerCapabilities != null && mcpServerCapabilities.getTools() == null) { + logger.debug("Skipping tools discovery as MCP server capabilities do not support tools."); + return responseParamsList; + } - List responseParamsList = new ArrayList<>(); - try { - if (mcpServerCapabilities != null && mcpServerCapabilities.getResources() == null) { - logger.debug("Skipping resources discovery as MCP server capabilities do not support resources."); - return responseParamsList; - } - Pair resourcesListResponsePair = getMcpMethodResponse( - host, McpSchema.METHOD_RESOURCES_LIST, MCP_RESOURCE_LIST_REQUEST_JSON, apiCollection); + Pair toolsListResponsePair = getMcpMethodResponse( + host, McpSchema.METHOD_TOOLS_LIST, MCP_TOOLS_LIST_REQUEST_JSON, apiCollection); - if (resourcesListResponsePair.getSecond() != null && !normalizedSampleDataSet.contains( - McpSchema.METHOD_RESOURCES_LIST)) { - responseParamsList.add(resourcesListResponsePair.getSecond()); + if (toolsListResponsePair.getSecond() != null && !normalizedSampleDataSet.contains( + McpSchema.METHOD_TOOLS_LIST)) { + responseParamsList.add(toolsListResponsePair.getSecond()); + } + logger.debug("Received tools/list response. Processing tools....."); + + ListToolsResult toolsResult = JSONUtils.fromJson(toolsListResponsePair.getFirst().getResult(), + ListToolsResult.class); + + if (toolsResult != null && !CollectionUtils.isEmpty(toolsResult.getTools())) { + int id = 2; + String urlWithQueryParams = toolsListResponsePair.getSecond().getRequestParams().getURL(); + String toolsCallRequestHeaders = buildHeaders(host); + + for (Tool tool : toolsResult.getTools()) { + if (normalizedSampleDataSet.contains(McpSchema.METHOD_TOOLS_CALL + "/" + tool.getName())) { + logger.debug("Skipping tool {} as it is already present in the db.", tool.getName()); + continue; + } + JSONRPCRequest request = new JSONRPCRequest( + McpSchema.JSONRPC_VERSION, + McpSchema.METHOD_TOOLS_CALL, + id++, + new CallToolRequest(tool.getName(), generateExampleArguments(tool.getInputSchema())) + ); + + HttpResponseParams toolsCallHttpResponseParams = convertToAktoFormat(apiCollection.getId(), + urlWithQueryParams, + toolsCallRequestHeaders, + HttpMethod.POST.name(), + mapper.writeValueAsString(request), + new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); + + if (toolsCallHttpResponseParams != null) { + responseParamsList.add(toolsCallHttpResponseParams); + } + } + } else { + logger.debug("Skipping as List Tools Result is null or Tools are empty"); + } + } catch (Exception e) { + logger.error("Error while discovering mcp tools for hostname: {}", host, e); } - logger.debug("Received resources/list response. Processing resources....."); + return responseParamsList; + } + } - ListResourcesResult resourcesResult = JSONUtils.fromJson(resourcesListResponsePair.getFirst().getResult(), - ListResourcesResult.class); + public List handleMcpResourceDiscovery(ApiCollection apiCollection, + Set normalizedSampleDataSet, boolean isRecon, McpServer mcpServer) { - if (resourcesResult != null && !CollectionUtils.isEmpty(resourcesResult.getResources())) { - int id = 2; - String urlWithQueryParams = resourcesListResponsePair.getSecond().getRequestParams().getURL(); - String toolsCallRequestHeaders = buildHeaders(host); + String host =""; + List responseParamsList = new ArrayList<>(); - for (Resource resource : resourcesResult.getResources()) { - if (normalizedSampleDataSet.contains(McpSchema.METHOD_RESOURCES_READ + "/" + resource.getUri())) { - logger.debug("Skipping resource {} as it is already present in the db.", resource.getUri()); - continue; - } - JSONRPCRequest request = new JSONRPCRequest( - McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_RESOURCES_READ, - id++, - new ReadResourceRequest(resource.getUri()) + if(isRecon) { + try { + int id = 1; + String resourceCallRequestHeaders = buildHeaders(host); + for (McpSchema.Resource resource : mcpServer.getResources()) { + McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest( + McpSchema.JSONRPC_VERSION, + McpSchema.METHOD_RESOURCES_READ, + id++, + new McpSchema.ReadResourceRequest(resource.getUri()) ); - HttpResponseParams readResourceHttpResponseParams = convertToAktoFormat(apiCollection.getId(), - urlWithQueryParams, - toolsCallRequestHeaders, - HttpMethod.POST.name(), - mapper.writeValueAsString(request), - new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); + HttpResponseParams readResourceHttpResponseParams = McpToolsSyncJobExecutor.convertToAktoFormat(0, + mcpServer.getUrl(), + resourceCallRequestHeaders, + HttpMethod.POST.name(), + mapper.writeValueAsString(request), + new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); if (readResourceHttpResponseParams != null) { responseParamsList.add(readResourceHttpResponseParams); } } - } else { - logger.debug("Skipping as List Resource Result is null or Resources are empty"); + } catch (Exception e) { + logger.error("Error while discovering mcp resources for hostname: {}", host, e); + } + return responseParamsList; + } + else { + host = apiCollection.getHostName(); + try { + if (mcpServerCapabilities != null && mcpServerCapabilities.getResources() == null) { + logger.debug("Skipping resources discovery as MCP server capabilities do not support resources."); + return responseParamsList; + } + Pair resourcesListResponsePair = getMcpMethodResponse( + host, McpSchema.METHOD_RESOURCES_LIST, MCP_RESOURCE_LIST_REQUEST_JSON, apiCollection); + + if (resourcesListResponsePair.getSecond() != null && !normalizedSampleDataSet.contains( + McpSchema.METHOD_RESOURCES_LIST)) { + responseParamsList.add(resourcesListResponsePair.getSecond()); + } + logger.debug("Received resources/list response. Processing resources....."); + + ListResourcesResult resourcesResult = JSONUtils.fromJson(resourcesListResponsePair.getFirst().getResult(), + ListResourcesResult.class); + + if (resourcesResult != null && !CollectionUtils.isEmpty(resourcesResult.getResources())) { + int id = 2; + String urlWithQueryParams = resourcesListResponsePair.getSecond().getRequestParams().getURL(); + String toolsCallRequestHeaders = buildHeaders(host); + + for (Resource resource : resourcesResult.getResources()) { + if (normalizedSampleDataSet.contains(McpSchema.METHOD_RESOURCES_READ + "/" + resource.getUri())) { + logger.debug("Skipping resource {} as it is already present in the db.", resource.getUri()); + continue; + } + JSONRPCRequest request = new JSONRPCRequest( + McpSchema.JSONRPC_VERSION, + McpSchema.METHOD_RESOURCES_READ, + id++, + new ReadResourceRequest(resource.getUri()) + ); + + HttpResponseParams readResourceHttpResponseParams = convertToAktoFormat(apiCollection.getId(), + urlWithQueryParams, + toolsCallRequestHeaders, + HttpMethod.POST.name(), + mapper.writeValueAsString(request), + new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); + + if (readResourceHttpResponseParams != null) { + responseParamsList.add(readResourceHttpResponseParams); + } + } + } else { + logger.debug("Skipping as List Resource Result is null or Resources are empty"); + } + } catch (Exception e) { + logger.error("Error while discovering mcp resources for hostname: {}", host, e); } - } catch (Exception e) { - logger.error("Error while discovering mcp resources for hostname: {}", host, e); + return responseParamsList; } - return responseParamsList; } - public static void processResponseParams(APIConfig apiConfig, List responseParamsList) { + public static void processResponseParams(APIConfig apiConfig, List responseParamsList, boolean isRecon) { if (CollectionUtils.isEmpty(responseParamsList)) { logger.debug("No response params to process for MCP sync job."); return; @@ -287,7 +353,8 @@ public static void processResponseParams(APIConfig apiConfig, List Date: Tue, 7 Oct 2025 10:59:33 +0530 Subject: [PATCH 2/3] MCP Recon Source check added --- .../akto/hybrid_parsers/HttpCallParser.java | 22 +++++++++---------- .../java/com/akto/hybrid_runtime/Main.java | 8 +++---- .../McpReconSyncJobExecutor.java | 5 +++-- .../McpToolsSyncJobExecutor.java | 9 ++++---- .../java/com/akto/dto/HttpResponseParams.java | 2 +- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/apps/mini-runtime/src/main/java/com/akto/hybrid_parsers/HttpCallParser.java b/apps/mini-runtime/src/main/java/com/akto/hybrid_parsers/HttpCallParser.java index 239ee2b20d..c5478a3b16 100644 --- a/apps/mini-runtime/src/main/java/com/akto/hybrid_parsers/HttpCallParser.java +++ b/apps/mini-runtime/src/main/java/com/akto/hybrid_parsers/HttpCallParser.java @@ -362,13 +362,13 @@ public static Pair applyAdvancedFilters(HttpResp return new Pair(responseParams, FILTER_TYPE.ERROR); } - public void syncFunction(List responseParams, boolean syncImmediately, boolean fetchAllSTI, AccountSettings accountSettings, boolean isRecon) { + public void syncFunction(List responseParams, boolean syncImmediately, boolean fetchAllSTI, AccountSettings accountSettings) { // USE ONLY filteredResponseParams and not responseParams List filteredResponseParams = responseParams; if (accountSettings != null && accountSettings.getDefaultPayloads() != null) { filteredResponseParams = filterDefaultPayloads(filteredResponseParams, accountSettings.getDefaultPayloads()); } - filteredResponseParams = filterHttpResponseParams(filteredResponseParams, accountSettings, isRecon); + filteredResponseParams = filterHttpResponseParams(filteredResponseParams, accountSettings); boolean isHarOrPcap = aggregate(filteredResponseParams, aggregatorMap); apiCatalogSync.setMergeUrlsOnVersions(accountSettings.isAllowMergingOnVersions()); @@ -508,12 +508,12 @@ private static void sendTrafficMetricsToTelemetry(BasicDBObject metricsData) { } } - public static boolean useHostCondition(String hostName, HttpResponseParams.Source source, boolean isRecon) { - List whiteListSource = Arrays.asList(HttpResponseParams.Source.MIRRORING); + public static boolean useHostCondition(String hostName, HttpResponseParams.Source source) { + List whiteListSource = Arrays.asList(HttpResponseParams.Source.MIRRORING, HttpResponseParams.Source.MCP_RECON); boolean hostNameCondition; if (hostName == null) { hostNameCondition = false; - } else if (isRecon) { + } else if (source.equals(HttpResponseParams.Source.MCP_RECON)) { hostNameCondition = true; } else { @@ -647,7 +647,7 @@ public void updateApiCollectionTags(String hostNameMapKey, HttpResponseParams ht } } - public int createApiCollectionId(HttpResponseParams httpResponseParam, boolean isRecon){ + public int createApiCollectionId(HttpResponseParams httpResponseParam){ int apiCollectionId; String hostName = getHeaderValue(httpResponseParam.getRequestParams().getHeaders(), "host"); @@ -659,7 +659,7 @@ public int createApiCollectionId(HttpResponseParams httpResponseParam, boolean i String vpcId = System.getenv("VPC_ID"); List tagList = CollectionTags.convertTagsFormat(httpResponseParam.getTags()); - if (useHostCondition(hostName, httpResponseParam.getSource(), isRecon)) { + if (useHostCondition(hostName, httpResponseParam.getSource())) { hostName = hostName.toLowerCase(); hostName = hostName.trim(); @@ -751,7 +751,7 @@ private boolean isBlankResponseBodyForGET(String method, String contentType, Str return res; } - public List filterHttpResponseParams(List httpResponseParamsList, AccountSettings accountSettings, boolean isRecon) { + public List filterHttpResponseParams(List httpResponseParamsList, AccountSettings accountSettings) { List filteredResponseParams = new ArrayList<>(); int originalSize = httpResponseParamsList.size(); @@ -763,7 +763,7 @@ public List filterHttpResponseParams(List> executorNodesMap = ParseAndExecute.createExecutorNodeMap(apiCatalogSync.advancedFilterMap); for (HttpResponseParams httpResponseParam: httpResponseParamsList) { - if (httpResponseParam.getSource().equals(HttpResponseParams.Source.MIRRORING)) { + if (httpResponseParam.getSource().equals(HttpResponseParams.Source.MIRRORING) || httpResponseParam.getSource().equals(HttpResponseParams.Source.MCP_RECON)) { TrafficMetrics.Key totalRequestsKey = getTrafficMetricsKey(httpResponseParam, TrafficMetrics.Name.TOTAL_REQUESTS_RUNTIME); incTrafficMetrics(totalRequestsKey,1); } @@ -868,7 +868,7 @@ public List filterHttpResponseParams(List filterHttpResponseParams(List> responseParamsToAccountMap, Map accountInfoMap, boolean isDashboardInstance, Map httpCallParserMap, APIConfig apiConfig, boolean fetchAllSTI, - boolean syncImmediately, String centralKafkaTopicName, boolean isRecon) { + boolean syncImmediately, String centralKafkaTopicName) { for (String accountId: responseParamsToAccountMap.keySet()) { int accountIdInt; try { @@ -690,7 +690,7 @@ public static void handleResponseParams(Map> re accWiseResponse = filterBasedOnHeaders(accWiseResponse, accountInfo.accountSettings); loggerMaker.infoAndAddToDb("Initiating sync function for account: " + accountId); - parser.syncFunction(accWiseResponse, syncImmediately, fetchAllSTI, accountInfo.accountSettings, isRecon); + parser.syncFunction(accWiseResponse, syncImmediately, fetchAllSTI, accountInfo.accountSettings); loggerMaker.debugInfoAddToDb("Sync function completed for account: " + accountId); sendToCentralKafka(centralKafkaTopicName, accWiseResponse); @@ -1047,7 +1047,7 @@ public static void processData(List data) { apiConfig, fetchAllSTI, syncImmediately, - centralKafkaTopicName, false); + centralKafkaTopicName); AllMetrics.instance.setRuntimeProcessLatency(System.currentTimeMillis()-start); } diff --git a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconSyncJobExecutor.java b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconSyncJobExecutor.java index 4db652c801..bbcee7f4da 100644 --- a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconSyncJobExecutor.java +++ b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpReconSyncJobExecutor.java @@ -338,7 +338,7 @@ private void processScanResults(List scanResults, APIConfig apiC responseParamsToProcess.addAll(toolsResponseList); responseParamsToProcess.addAll(resourcesResponseList); responseParamsToProcess.addAll(endpointsResponseList); - McpToolsSyncJobExecutor.processResponseParams(apiConfig, responseParamsToProcess, true); + McpToolsSyncJobExecutor.processResponseParams(apiConfig, responseParamsToProcess); // Insert when batch is full if (serverBatch.size() >= BATCH_SIZE) { @@ -515,7 +515,7 @@ public List handleMcpRequestDiscovery(McpServer mcpServer) { String requestHeaders = buildHeaders(host); McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest( McpSchema.JSONRPC_VERSION, - mcpServer.getEndpoint(), + McpSchema.METHOD_PING, String.valueOf(1), new McpSchema.InitializeRequest( McpSchema.LATEST_PROTOCOL_VERSION, @@ -536,6 +536,7 @@ public List handleMcpRequestDiscovery(McpServer mcpServer) { new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); if (requestHttpResponseParams != null) { + requestHttpResponseParams.setSource(HttpResponseParams.Source.MCP_RECON); responseParamsList.add(requestHttpResponseParams); } diff --git a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java index 9a1450fa34..46f58fe7e2 100644 --- a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java +++ b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java @@ -110,7 +110,7 @@ public void runJob(APIConfig apiConfig) { responseParamsToProcess.addAll(initResponseList); responseParamsToProcess.addAll(toolsResponseList); responseParamsToProcess.addAll(resourcesResponseList); - processResponseParams(apiConfig, responseParamsToProcess, false); + processResponseParams(apiConfig, responseParamsToProcess); } catch (Exception e) { logger.error("Error while running MCP sync job for apiCollectionId: {} and hostname: {}", apiCollection.getId(), apiCollection.getHostName(), e); @@ -181,6 +181,7 @@ public List handleMcpToolsDiscovery(ApiCollection apiCollect new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); if (toolsCallHttpResponseParams != null) { + toolsCallHttpResponseParams.setSource(Source.MCP_RECON); responseParamsList.add(toolsCallHttpResponseParams); } } @@ -273,6 +274,7 @@ public List handleMcpResourceDiscovery(ApiCollection apiColl new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); if (readResourceHttpResponseParams != null) { + readResourceHttpResponseParams.setSource(Source.MCP_RECON); responseParamsList.add(readResourceHttpResponseParams); } } @@ -338,7 +340,7 @@ public List handleMcpResourceDiscovery(ApiCollection apiColl } } - public static void processResponseParams(APIConfig apiConfig, List responseParamsList, boolean isRecon) { + public static void processResponseParams(APIConfig apiConfig, List responseParamsList) { if (CollectionUtils.isEmpty(responseParamsList)) { logger.debug("No response params to process for MCP sync job."); return; @@ -353,8 +355,7 @@ public static void processResponseParams(APIConfig apiConfig, List Date: Wed, 5 Nov 2025 16:34:38 +0530 Subject: [PATCH 3/3] MCP Recon code reuse --- .../McpToolsSyncJobExecutor.java | 192 ++++++++---------- 1 file changed, 85 insertions(+), 107 deletions(-) diff --git a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java index 46f58fe7e2..f7dca907c0 100644 --- a/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java +++ b/apps/mini-runtime/src/main/java/com/akto/hybrid_runtime/McpToolsSyncJobExecutor.java @@ -161,34 +161,7 @@ public List handleMcpToolsDiscovery(ApiCollection apiCollect List responseParamsList = new ArrayList<>(); if(isRecon) { host = mcpServer.getIp() + ":" + mcpServer.getPort(); - - try { - int id = 1; - String toolsCallRequestHeaders = buildHeaders(host); - for (McpSchema.Tool tool : mcpServer.getTools()) { - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest( - McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_TOOLS_CALL, - id++, - new McpSchema.CallToolRequest(tool.getName(), McpToolsSyncJobExecutor.generateExampleArguments(tool.getInputSchema())) - ); - - HttpResponseParams toolsCallHttpResponseParams = McpToolsSyncJobExecutor.convertToAktoFormat(0, - mcpServer.getUrl(), - toolsCallRequestHeaders, - HttpMethod.POST.name(), - mapper.writeValueAsString(request), - new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); - - if (toolsCallHttpResponseParams != null) { - toolsCallHttpResponseParams.setSource(Source.MCP_RECON); - responseParamsList.add(toolsCallHttpResponseParams); - } - } - } catch (Exception e) { - logger.error("Error while discovering mcp tools for hostname: {}", host, e); - } - return responseParamsList; + responseParamsList = getHttpResponseParamsforTools(mcpServer.getTools(), mcpServer.getUrl(), Source.MCP_RECON, host, Collections.EMPTY_SET); } else { host = apiCollection.getHostName(); @@ -213,39 +186,54 @@ public List handleMcpToolsDiscovery(ApiCollection apiCollect if (toolsResult != null && !CollectionUtils.isEmpty(toolsResult.getTools())) { int id = 2; String urlWithQueryParams = toolsListResponsePair.getSecond().getRequestParams().getURL(); - String toolsCallRequestHeaders = buildHeaders(host); - - for (Tool tool : toolsResult.getTools()) { - if (normalizedSampleDataSet.contains(McpSchema.METHOD_TOOLS_CALL + "/" + tool.getName())) { - logger.debug("Skipping tool {} as it is already present in the db.", tool.getName()); - continue; - } - JSONRPCRequest request = new JSONRPCRequest( - McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_TOOLS_CALL, - id++, - new CallToolRequest(tool.getName(), generateExampleArguments(tool.getInputSchema())) - ); - - HttpResponseParams toolsCallHttpResponseParams = convertToAktoFormat(apiCollection.getId(), - urlWithQueryParams, - toolsCallRequestHeaders, - HttpMethod.POST.name(), - mapper.writeValueAsString(request), - new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); - - if (toolsCallHttpResponseParams != null) { - responseParamsList.add(toolsCallHttpResponseParams); - } - } + responseParamsList = getHttpResponseParamsforTools(toolsResult.getTools(), urlWithQueryParams, Source.MIRRORING, host, normalizedSampleDataSet); } else { logger.debug("Skipping as List Tools Result is null or Tools are empty"); } } catch (Exception e) { logger.error("Error while discovering mcp tools for hostname: {}", host, e); } - return responseParamsList; + } + return responseParamsList; + } + + private List getHttpResponseParamsforTools(List tools, String url, Source source, String host, + Set normalizedSampleDataSet) { + List responseParamsList = new ArrayList<>(); + try { + int id = 1; + String toolsCallRequestHeaders = buildHeaders(host); + for (Tool tool : tools) { + + if (!normalizedSampleDataSet.isEmpty() && normalizedSampleDataSet.contains(McpSchema.METHOD_TOOLS_CALL + "/" + tool.getName())) { + logger.debug("Skipping tool {} as it is already present in the db.", tool.getName()); + continue; + } + + JSONRPCRequest request = new JSONRPCRequest( + McpSchema.JSONRPC_VERSION, + McpSchema.METHOD_TOOLS_CALL, + id++, + new CallToolRequest(tool.getName(), McpToolsSyncJobExecutor.generateExampleArguments(tool.getInputSchema())) + ); + + HttpResponseParams toolsCallHttpResponseParams = McpToolsSyncJobExecutor.convertToAktoFormat(0, + url, + toolsCallRequestHeaders, + HttpMethod.POST.name(), + mapper.writeValueAsString(request), + new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); + + if (toolsCallHttpResponseParams != null) { + toolsCallHttpResponseParams.setSource(source); + responseParamsList.add(toolsCallHttpResponseParams); + } + } + } catch (Exception e) { + logger.error("Error while discovering mcp tools for hostname: {}", host, e); + } + return responseParamsList; } public List handleMcpResourceDiscovery(ApiCollection apiCollection, @@ -255,33 +243,8 @@ public List handleMcpResourceDiscovery(ApiCollection apiColl List responseParamsList = new ArrayList<>(); if(isRecon) { - try { - int id = 1; - String resourceCallRequestHeaders = buildHeaders(host); - for (McpSchema.Resource resource : mcpServer.getResources()) { - McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest( - McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_RESOURCES_READ, - id++, - new McpSchema.ReadResourceRequest(resource.getUri()) - ); - - HttpResponseParams readResourceHttpResponseParams = McpToolsSyncJobExecutor.convertToAktoFormat(0, - mcpServer.getUrl(), - resourceCallRequestHeaders, - HttpMethod.POST.name(), - mapper.writeValueAsString(request), - new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); - - if (readResourceHttpResponseParams != null) { - readResourceHttpResponseParams.setSource(Source.MCP_RECON); - responseParamsList.add(readResourceHttpResponseParams); - } - } - } catch (Exception e) { - logger.error("Error while discovering mcp resources for hostname: {}", host, e); - } - return responseParamsList; + host = mcpServer.getIp() + ":" + mcpServer.getPort(); + responseParamsList = getHttpResponseParamsforResource(mcpServer.getResources(), mcpServer.getUrl(), Source.MCP_RECON, host, Collections.EMPTY_SET); } else { host = apiCollection.getHostName(); @@ -305,39 +268,54 @@ public List handleMcpResourceDiscovery(ApiCollection apiColl if (resourcesResult != null && !CollectionUtils.isEmpty(resourcesResult.getResources())) { int id = 2; String urlWithQueryParams = resourcesListResponsePair.getSecond().getRequestParams().getURL(); - String toolsCallRequestHeaders = buildHeaders(host); - - for (Resource resource : resourcesResult.getResources()) { - if (normalizedSampleDataSet.contains(McpSchema.METHOD_RESOURCES_READ + "/" + resource.getUri())) { - logger.debug("Skipping resource {} as it is already present in the db.", resource.getUri()); - continue; - } - JSONRPCRequest request = new JSONRPCRequest( - McpSchema.JSONRPC_VERSION, - McpSchema.METHOD_RESOURCES_READ, - id++, - new ReadResourceRequest(resource.getUri()) - ); - - HttpResponseParams readResourceHttpResponseParams = convertToAktoFormat(apiCollection.getId(), - urlWithQueryParams, - toolsCallRequestHeaders, - HttpMethod.POST.name(), - mapper.writeValueAsString(request), - new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); - - if (readResourceHttpResponseParams != null) { - responseParamsList.add(readResourceHttpResponseParams); - } - } + + responseParamsList = getHttpResponseParamsforResource(resourcesResult.getResources(), urlWithQueryParams, Source.MIRRORING, host, normalizedSampleDataSet); } else { logger.debug("Skipping as List Resource Result is null or Resources are empty"); } } catch (Exception e) { logger.error("Error while discovering mcp resources for hostname: {}", host, e); } - return responseParamsList; + + } + return responseParamsList; + } + + private List getHttpResponseParamsforResource(List resources, String url, Source source, String host, Set normalizedSampleDataSet) { + List responseParamsList = new ArrayList<>(); + try { + int id = 1; + String resourceCallRequestHeaders = buildHeaders(host); + for (Resource resource : resources) { + + if (normalizedSampleDataSet.contains(McpSchema.METHOD_RESOURCES_READ + "/" + resource.getUri())) { + logger.debug("Skipping resource {} as it is already present in the db.", resource.getUri()); + continue; + } + + JSONRPCRequest request = new JSONRPCRequest( + McpSchema.JSONRPC_VERSION, + McpSchema.METHOD_RESOURCES_READ, + id++, + new ReadResourceRequest(resource.getUri()) + ); + + HttpResponseParams readResourceHttpResponseParams = McpToolsSyncJobExecutor.convertToAktoFormat(0, + url, + resourceCallRequestHeaders, + HttpMethod.POST.name(), + mapper.writeValueAsString(request), + new OriginalHttpResponse("", Collections.emptyMap(), HttpStatus.SC_OK)); + + if (readResourceHttpResponseParams != null) { + readResourceHttpResponseParams.setSource(source); + responseParamsList.add(readResourceHttpResponseParams); + } + } + } catch (Exception e) { + logger.error("Error while discovering mcp resources for hostname: {}", host, e); } + return responseParamsList; } public static void processResponseParams(APIConfig apiConfig, List responseParamsList) {