This is an automated email from the ASF dual-hosted git repository.
ldemasi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 8791829353d4 CAMEL-23197: [camel-jbang-mcp] Extract shared
CatalogService with caching and add version parameters to all catalog tools
8791829353d4 is described below
commit 8791829353d448c2424628b56c020a7d5d1e9619
Author: Luigi De Masi <[email protected]>
AuthorDate: Tue Mar 17 08:06:07 2026 +0100
CAMEL-23197: [camel-jbang-mcp] Extract shared CatalogService with caching
and add version parameters to all catalog tools
---
.../jbang/core/commands/mcp/CatalogService.java | 175 +++++++++++++++++++++
.../dsl/jbang/core/commands/mcp/CatalogTools.java | 145 +++++++----------
.../core/commands/mcp/DependencyCheckTools.java | 26 +--
.../dsl/jbang/core/commands/mcp/DiagnoseTools.java | 26 +--
.../dsl/jbang/core/commands/mcp/ExplainTools.java | 25 +--
.../dsl/jbang/core/commands/mcp/HardenTools.java | 18 ++-
.../jbang/core/commands/mcp/TestScaffoldTools.java | 29 ++--
.../jbang/core/commands/mcp/TransformTools.java | 22 +--
.../jbang/core/commands/mcp/CatalogToolsTest.java | 5 +-
.../commands/mcp/DependencyCheckToolsTest.java | 41 ++---
.../jbang/core/commands/mcp/DiagnoseToolsTest.java | 43 ++---
.../core/commands/mcp/TestScaffoldToolsTest.java | 39 +++--
12 files changed, 387 insertions(+), 207 deletions(-)
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogService.java
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogService.java
new file mode 100644
index 000000000000..a0e497ce9c2c
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogService.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.mcp;
+
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+import io.quarkiverse.mcp.server.ToolCallException;
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.dsl.jbang.core.common.CatalogLoader;
+import org.apache.camel.dsl.jbang.core.common.RuntimeType;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+/**
+ * Shared service for loading and caching Camel catalogs.
+ * <p>
+ * All MCP tool classes that need a {@link CamelCatalog} should inject this
service instead of creating their own
+ * {@link DefaultCamelCatalog} instances. This ensures consistent version
handling and avoids redundant Maven artifact
+ * downloads when multiple tools query the same version.
+ * <p>
+ * Catalogs are cached by {@code (runtime, camelVersion, platformBom)} tuple.
The default catalog (no version
+ * parameters) is created once at startup.
+ */
+@ApplicationScoped
+public class CatalogService {
+
+ @ConfigProperty(name = "camel.catalog.repos")
+ Optional<String> catalogRepos;
+
+ private final CamelCatalog defaultCatalog;
+ private final ConcurrentMap<CatalogKey, CamelCatalog> cache = new
ConcurrentHashMap<>();
+
+ public CatalogService() {
+ this.defaultCatalog = new DefaultCamelCatalog(true);
+ }
+
+ /**
+ * Get the default catalog (built-in version, no specific runtime or
version requested).
+ */
+ public CamelCatalog getDefaultCatalog() {
+ return defaultCatalog;
+ }
+
+ /**
+ * Load a catalog for the given runtime, version, and platform BOM.
Results are cached by the
+ * {@code (runtime, camelVersion, platformBom)} tuple so that repeated
calls with the same parameters do not trigger
+ * redundant Maven downloads.
+ *
+ * @param runtime runtime type: "main", "spring-boot", or "quarkus"
(default: main)
+ * @param camelVersion the Camel version to query, or null for the default
+ * @param platformBom platform BOM in GAV format
(groupId:artifactId:version), or null
+ * @return the loaded (and possibly cached) CamelCatalog
+ * @throws Exception if catalog loading fails
+ */
+ public CamelCatalog loadCatalog(String runtime, String camelVersion,
String platformBom) throws Exception {
+ RuntimeType runtimeType = resolveRuntime(runtime);
+
+ boolean hasVersion = camelVersion != null && !camelVersion.isBlank();
+ boolean hasBom = platformBom != null && !platformBom.isBlank();
+
+ // No version-specific parameters and main runtime -> default catalog
+ if (!hasVersion && !hasBom && runtimeType == RuntimeType.main) {
+ return defaultCatalog;
+ }
+
+ // Normalize the cache key
+ String normalizedVersion = hasVersion ? camelVersion : null;
+ String normalizedBom = hasBom ? platformBom : null;
+
+ // For non-main runtimes without explicit version, use the runtime's
default version
+ if (!hasVersion && !hasBom) {
+ if (runtimeType == RuntimeType.quarkus) {
+ normalizedVersion = RuntimeType.QUARKUS_VERSION;
+ }
+ }
+
+ CatalogKey key = new CatalogKey(runtimeType.name(), normalizedVersion,
normalizedBom);
+
+ CamelCatalog cached = cache.get(key);
+ if (cached != null) {
+ return cached;
+ }
+
+ CamelCatalog loaded = doLoadCatalog(runtimeType, camelVersion,
platformBom);
+ cache.putIfAbsent(key, loaded);
+ return cache.get(key);
+ }
+
+ /**
+ * Resolve a runtime string to a {@link RuntimeType} enum value.
+ *
+ * @param runtime the runtime string ("main", "spring-boot",
"quarkus"), or null for main
+ * @return the resolved RuntimeType
+ * @throws ToolCallException if the runtime value is not recognized
+ */
+ public RuntimeType resolveRuntime(String runtime) {
+ if (runtime == null || runtime.isBlank() ||
"main".equalsIgnoreCase(runtime)) {
+ return RuntimeType.main;
+ }
+ try {
+ return RuntimeType.fromValue(runtime);
+ } catch (IllegalArgumentException e) {
+ throw new ToolCallException(
+ "Unsupported runtime: " + runtime + ". Supported values
are: main, spring-boot, quarkus", null);
+ }
+ }
+
+ private CamelCatalog doLoadCatalog(
+ RuntimeType runtimeType, String camelVersion, String platformBom)
+ throws Exception {
+
+ String repos = catalogRepos.orElse(null);
+
+ // If platformBom is provided (GAV format), parse and use it
+ if (platformBom != null && !platformBom.isBlank()) {
+ String[] parts = platformBom.split(":");
+ if (parts.length != 3) {
+ throw new ToolCallException(
+ "platformBom must be in GAV format
(groupId:artifactId:version), got: " + platformBom, null);
+ }
+ String groupId = parts[0];
+ String artifactId = parts[1];
+ String version = parts[2];
+
+ if (runtimeType == RuntimeType.quarkus) {
+ return CatalogLoader.loadQuarkusCatalog(repos, version,
groupId, artifactId, true);
+ } else if (runtimeType == RuntimeType.springBoot) {
+ return CatalogLoader.loadSpringBootCatalog(repos, version,
groupId, true);
+ } else {
+ return CatalogLoader.loadCatalog(repos, version, groupId,
true);
+ }
+ }
+
+ // If a specific version is requested, load that version's catalog
+ if (camelVersion != null && !camelVersion.isBlank()) {
+ if (runtimeType == RuntimeType.springBoot) {
+ return CatalogLoader.loadSpringBootCatalog(repos,
camelVersion, true);
+ } else if (runtimeType == RuntimeType.quarkus) {
+ return CatalogLoader.loadQuarkusCatalog(repos, camelVersion,
null, true);
+ } else {
+ return CatalogLoader.loadCatalog(repos, camelVersion, true);
+ }
+ }
+
+ // No specific version, use runtime-specific default catalog
+ if (runtimeType == RuntimeType.springBoot) {
+ return CatalogLoader.loadSpringBootCatalog(repos, null, true);
+ } else if (runtimeType == RuntimeType.quarkus) {
+ return CatalogLoader.loadQuarkusCatalog(repos,
RuntimeType.QUARKUS_VERSION, null, true);
+ }
+
+ return defaultCatalog;
+ }
+
+ private record CatalogKey(String runtime, String camelVersion, String
platformBom) {
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogTools.java
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogTools.java
index c165c179c9d8..4b6ea3992c0a 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogTools.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogTools.java
@@ -18,23 +18,19 @@ package org.apache.camel.dsl.jbang.core.commands.mcp;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import java.util.stream.Collectors;
import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
import io.quarkiverse.mcp.server.Tool;
import io.quarkiverse.mcp.server.ToolArg;
import io.quarkiverse.mcp.server.ToolCallException;
import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
-import org.apache.camel.dsl.jbang.core.common.CatalogLoader;
-import org.apache.camel.dsl.jbang.core.common.RuntimeType;
import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.tooling.model.DataFormatModel;
import org.apache.camel.tooling.model.EipModel;
import org.apache.camel.tooling.model.LanguageModel;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
/**
* MCP Tools for querying the Camel Catalog using Quarkus MCP Server.
@@ -42,14 +38,8 @@ import org.eclipse.microprofile.config.inject.ConfigProperty;
@ApplicationScoped
public class CatalogTools {
- @ConfigProperty(name = "camel.catalog.repos")
- Optional<String> catalogRepos;
-
- private CamelCatalog catalog;
-
- public CatalogTools() {
- this.catalog = new DefaultCamelCatalog(true);
- }
+ @Inject
+ CatalogService catalogService;
/**
* Tool to list available Camel components.
@@ -74,7 +64,7 @@ public class CatalogTools {
int maxResults = limit != null ? limit : 50;
try {
- CamelCatalog cat = loadCatalog(runtime, camelVersion, platformBom);
+ CamelCatalog cat = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
List<ComponentInfo> components = findComponentNames(cat).stream()
.map(cat::componentModel)
@@ -121,7 +111,7 @@ public class CatalogTools {
}
try {
- CamelCatalog cat = loadCatalog(runtime, camelVersion, platformBom);
+ CamelCatalog cat = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
ComponentModel model = cat.componentModel(component);
if (model == null) {
// Check if it might be a data format or language instead
@@ -168,13 +158,19 @@ public class CatalogTools {
"(e.g., json, xml, csv, avro, protobuf).")
public DataFormatListResult camel_catalog_dataformats(
@ToolArg(description = "Filter by name") String filter,
- @ToolArg(description = "Maximum results (default: 50)") Integer
limit) {
+ @ToolArg(description = "Maximum results (default: 50)") Integer
limit,
+ @ToolArg(description = "Runtime type: main, spring-boot, or
quarkus (default: main)") String runtime,
+ @ToolArg(description = "Camel version to use (e.g., 4.17.0). If
not specified, uses the default catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion.")
String platformBom) {
int maxResults = limit != null ? limit : 50;
try {
- List<DataFormatInfo> dataFormats =
catalog.findDataFormatNames().stream()
- .map(catalog::dataFormatModel)
+ CamelCatalog cat = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
+
+ List<DataFormatInfo> dataFormats =
cat.findDataFormatNames().stream()
+ .map(cat::dataFormatModel)
.filter(m -> m != null)
.filter(m -> matchesFilter(m.getName(), m.getTitle(),
m.getDescription(), filter))
.limit(maxResults)
@@ -194,11 +190,17 @@ public class CatalogTools {
@Tool(description = "List available Camel expression languages " +
"(e.g., simple, jsonpath, xpath, groovy, jq).")
public LanguageListResult camel_catalog_languages(
- @ToolArg(description = "Filter by name") String filter) {
+ @ToolArg(description = "Filter by name") String filter,
+ @ToolArg(description = "Runtime type: main, spring-boot, or
quarkus (default: main)") String runtime,
+ @ToolArg(description = "Camel version to use (e.g., 4.17.0). If
not specified, uses the default catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion.")
String platformBom) {
try {
- List<LanguageInfo> languages = catalog.findLanguageNames().stream()
- .map(catalog::languageModel)
+ CamelCatalog cat = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
+
+ List<LanguageInfo> languages = cat.findLanguageNames().stream()
+ .map(cat::languageModel)
.filter(m -> m != null)
.filter(m -> matchesFilter(m.getName(), m.getTitle(),
m.getDescription(), filter))
.map(this::toLanguageInfo)
@@ -217,14 +219,20 @@ public class CatalogTools {
@Tool(description = "Get detailed documentation for a Camel data format
including all options, "
+ "Maven coordinates, and configuration parameters.")
public DataFormatDetailResult camel_catalog_dataformat_doc(
- @ToolArg(description = "Data format name (e.g., json-jackson,
avro, csv, protobuf, jaxb)") String dataformat) {
+ @ToolArg(description = "Data format name (e.g., json-jackson,
avro, csv, protobuf, jaxb)") String dataformat,
+ @ToolArg(description = "Runtime type: main, spring-boot, or
quarkus (default: main)") String runtime,
+ @ToolArg(description = "Camel version to use (e.g., 4.17.0). If
not specified, uses the default catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion.")
String platformBom) {
if (dataformat == null || dataformat.isBlank()) {
throw new ToolCallException("Data format name is required", null);
}
try {
- DataFormatModel model = catalog.dataFormatModel(dataformat);
+ CamelCatalog cat = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
+
+ DataFormatModel model = cat.dataFormatModel(dataformat);
if (model == null) {
throw new ToolCallException("Data format not found: " +
dataformat, null);
}
@@ -245,14 +253,20 @@ public class CatalogTools {
@Tool(description = "Get detailed documentation for a Camel expression
language including all options, "
+ "Maven coordinates, and configuration parameters.")
public LanguageDetailResult camel_catalog_language_doc(
- @ToolArg(description = "Language name (e.g., simple, jsonpath,
xpath, jq, groovy)") String language) {
+ @ToolArg(description = "Language name (e.g., simple, jsonpath,
xpath, jq, groovy)") String language,
+ @ToolArg(description = "Runtime type: main, spring-boot, or
quarkus (default: main)") String runtime,
+ @ToolArg(description = "Camel version to use (e.g., 4.17.0). If
not specified, uses the default catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion.")
String platformBom) {
if (language == null || language.isBlank()) {
throw new ToolCallException("Language name is required", null);
}
try {
- LanguageModel model = catalog.languageModel(language);
+ CamelCatalog cat = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
+
+ LanguageModel model = cat.languageModel(language);
if (model == null) {
throw new ToolCallException("Language not found: " + language,
null);
}
@@ -273,11 +287,17 @@ public class CatalogTools {
"filter, choice, multicast, circuit-breaker, etc.")
public EipListResult camel_catalog_eips(
@ToolArg(description = "Filter by name") String filter,
- @ToolArg(description = "Filter by category (e.g., routing,
transformation, error handling)") String label) {
+ @ToolArg(description = "Filter by category (e.g., routing,
transformation, error handling)") String label,
+ @ToolArg(description = "Runtime type: main, spring-boot, or
quarkus (default: main)") String runtime,
+ @ToolArg(description = "Camel version to use (e.g., 4.17.0). If
not specified, uses the default catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion.")
String platformBom) {
try {
- List<EipInfo> eips = catalog.findModelNames().stream()
- .map(catalog::eipModel)
+ CamelCatalog cat = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
+
+ List<EipInfo> eips = cat.findModelNames().stream()
+ .map(cat::eipModel)
.filter(m -> m != null)
.filter(m -> matchesFilter(m.getName(), m.getTitle(),
m.getDescription(), filter))
.filter(m -> matchesLabel(m.getLabel(), label))
@@ -296,14 +316,20 @@ public class CatalogTools {
*/
@Tool(description = "Get detailed documentation for a Camel EIP
(Enterprise Integration Pattern).")
public EipDetailResult camel_catalog_eip_doc(
- @ToolArg(description = "EIP name (e.g., split, aggregate, choice,
filter)") String eip) {
+ @ToolArg(description = "EIP name (e.g., split, aggregate, choice,
filter)") String eip,
+ @ToolArg(description = "Runtime type: main, spring-boot, or
quarkus (default: main)") String runtime,
+ @ToolArg(description = "Camel version to use (e.g., 4.17.0). If
not specified, uses the default catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion.")
String platformBom) {
if (eip == null || eip.isBlank()) {
throw new ToolCallException("EIP name is required", null);
}
try {
- EipModel model = catalog.eipModel(eip);
+ CamelCatalog cat = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
+
+ EipModel model = cat.eipModel(eip);
if (model == null) {
throw new ToolCallException("EIP not found: " + eip, null);
}
@@ -317,65 +343,6 @@ public class CatalogTools {
}
}
- // Catalog loading
-
- private CamelCatalog loadCatalog(String runtime, String camelVersion,
String platformBom) throws Exception {
- String repos = catalogRepos.orElse(null);
- RuntimeType runtimeType = resolveRuntime(runtime);
-
- // If platformBom is provided (GAV format), parse and use it
- if (platformBom != null && !platformBom.isBlank()) {
- String[] parts = platformBom.split(":");
- if (parts.length != 3) {
- throw new ToolCallException(
- "platformBom must be in GAV format
(groupId:artifactId:version), got: " + platformBom, null);
- }
- String groupId = parts[0];
- String artifactId = parts[1];
- String version = parts[2];
-
- if (runtimeType == RuntimeType.quarkus) {
- return CatalogLoader.loadQuarkusCatalog(repos, version,
groupId, artifactId, true);
- } else if (runtimeType == RuntimeType.springBoot) {
- return CatalogLoader.loadSpringBootCatalog(repos, version,
groupId, true);
- } else {
- return CatalogLoader.loadCatalog(repos, version, groupId,
true);
- }
- }
-
- // If a specific version is requested, load that version's catalog
- if (camelVersion != null && !camelVersion.isBlank()) {
- if (runtimeType == RuntimeType.springBoot) {
- return CatalogLoader.loadSpringBootCatalog(repos,
camelVersion, true);
- } else if (runtimeType == RuntimeType.quarkus) {
- return CatalogLoader.loadQuarkusCatalog(repos, camelVersion,
null, true);
- } else {
- return CatalogLoader.loadCatalog(repos, camelVersion, true);
- }
- }
-
- // No specific version, use runtime-specific catalog or default
- if (runtimeType == RuntimeType.springBoot) {
- return CatalogLoader.loadSpringBootCatalog(repos, null, true);
- } else if (runtimeType == RuntimeType.quarkus) {
- return CatalogLoader.loadQuarkusCatalog(repos,
RuntimeType.QUARKUS_VERSION, null, true);
- }
-
- return catalog;
- }
-
- private RuntimeType resolveRuntime(String runtime) {
- if (runtime == null || runtime.isBlank() ||
"main".equalsIgnoreCase(runtime)) {
- return RuntimeType.main;
- }
- try {
- return RuntimeType.fromValue(runtime);
- } catch (IllegalArgumentException e) {
- throw new ToolCallException(
- "Unsupported runtime: " + runtime + ". Supported values
are: main, spring-boot, quarkus", null);
- }
- }
-
private static List<String> findComponentNames(CamelCatalog catalog) {
List<String> answer = catalog.findComponentNames();
if (answer == null) {
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
index a22bdef6b185..2c964e5a9162 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
@@ -25,7 +25,6 @@ import io.quarkiverse.mcp.server.Tool;
import io.quarkiverse.mcp.server.ToolArg;
import io.quarkiverse.mcp.server.ToolCallException;
import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.util.json.JsonArray;
import org.apache.camel.util.json.JsonObject;
@@ -40,13 +39,10 @@ import org.apache.camel.util.json.JsonObject;
public class DependencyCheckTools {
@Inject
- DependencyData dependencyData;
-
- private final CamelCatalog catalog;
+ CatalogService catalogService;
- public DependencyCheckTools() {
- this.catalog = new DefaultCamelCatalog();
- }
+ @Inject
+ DependencyData dependencyData;
/**
* Tool to check Camel dependency hygiene for a project.
@@ -59,13 +55,19 @@ public class DependencyCheckTools {
public String camel_dependency_check(
@ToolArg(description = "The pom.xml file content") String
pomContent,
@ToolArg(description = "Route definitions (YAML, XML, or Java DSL)
to check for missing component dependencies. "
- + "Multiple routes can be provided
concatenated.") String routes) {
+ + "Multiple routes can be provided
concatenated.") String routes,
+ @ToolArg(description = "Runtime type: main, spring-boot, or
quarkus (default: main)") String runtime,
+ @ToolArg(description = "Camel version to use (e.g., 4.17.0). If
not specified, uses the default catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion.")
String platformBom) {
if (pomContent == null || pomContent.isBlank()) {
throw new ToolCallException("pomContent is required", null);
}
try {
+ CamelCatalog catalog = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
+
MigrationData.PomAnalysis pom =
MigrationData.parsePomContent(pomContent);
JsonObject result = new JsonObject();
@@ -79,13 +81,13 @@ public class DependencyCheckTools {
result.put("projectInfo", projectInfo);
// 1. Check for outdated Camel version
- JsonObject versionCheck = checkVersionStatus(pom);
+ JsonObject versionCheck = checkVersionStatus(pom, catalog);
result.put("versionStatus", versionCheck);
// 2. Check for missing dependencies from routes
JsonArray missingDeps = new JsonArray();
if (routes != null && !routes.isBlank()) {
- missingDeps = checkMissingDependencies(routes,
pom.dependencies());
+ missingDeps = checkMissingDependencies(routes,
pom.dependencies(), catalog);
}
result.put("missingDependencies", missingDeps);
@@ -120,7 +122,7 @@ public class DependencyCheckTools {
/**
* Check if the project's Camel version is outdated compared to the
catalog version.
*/
- private JsonObject checkVersionStatus(MigrationData.PomAnalysis pom) {
+ private JsonObject checkVersionStatus(MigrationData.PomAnalysis pom,
CamelCatalog catalog) {
JsonObject check = new JsonObject();
String catalogVersion = catalog.getCatalogVersion();
check.put("catalogVersion", catalogVersion);
@@ -158,7 +160,7 @@ public class DependencyCheckTools {
/**
* Check for components used in routes that are missing from the project's
dependencies.
*/
- private JsonArray checkMissingDependencies(String routes, List<String>
existingDeps) {
+ private JsonArray checkMissingDependencies(String routes, List<String>
existingDeps, CamelCatalog catalog) {
JsonArray missing = new JsonArray();
String lowerRoutes = routes.toLowerCase();
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseTools.java
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseTools.java
index ca21d682126a..2a5abc1078f5 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseTools.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseTools.java
@@ -28,7 +28,6 @@ import io.quarkiverse.mcp.server.Tool;
import io.quarkiverse.mcp.server.ToolArg;
import io.quarkiverse.mcp.server.ToolCallException;
import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.tooling.model.EipModel;
import org.apache.camel.util.json.JsonArray;
@@ -65,13 +64,10 @@ public class DiagnoseTools {
= Pattern.compile("route[:\\s]+['\"]?([a-zA-Z0-9_-]+)['\"]?",
Pattern.CASE_INSENSITIVE);
@Inject
- DiagnoseData diagnoseData;
-
- private final CamelCatalog catalog;
+ CatalogService catalogService;
- public DiagnoseTools() {
- this.catalog = new DefaultCamelCatalog();
- }
+ @Inject
+ DiagnoseData diagnoseData;
/**
* Tool to diagnose Camel errors from stack traces or error messages.
@@ -82,13 +78,19 @@ public class DiagnoseTools {
+ "Covers the most common Camel exceptions including
NoSuchEndpointException, "
+ "ResolveEndpointFailedException,
FailedToCreateRouteException, and more.")
public String camel_error_diagnose(
- @ToolArg(description = "The Camel stack trace or error message to
diagnose") String error) {
+ @ToolArg(description = "The Camel stack trace or error message to
diagnose") String error,
+ @ToolArg(description = "Runtime type: main, spring-boot, or
quarkus (default: main)") String runtime,
+ @ToolArg(description = "Camel version to use (e.g., 4.17.0). If
not specified, uses the default catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion.")
String platformBom) {
if (error == null || error.isBlank()) {
throw new ToolCallException("Error message or stack trace is
required", null);
}
try {
+ CamelCatalog catalog = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
+
JsonObject result = new JsonObject();
// Identify matching exceptions
@@ -105,7 +107,7 @@ public class DiagnoseTools {
result.put("identifiedExceptions", exceptionsJson);
// Identify components from the error
- List<String> componentNames = extractComponentNames(error);
+ List<String> componentNames = extractComponentNames(error,
catalog);
JsonArray componentsJson = new JsonArray();
for (String comp : componentNames) {
ComponentModel model = catalog.componentModel(comp);
@@ -121,7 +123,7 @@ public class DiagnoseTools {
result.put("identifiedComponents", componentsJson);
// Identify EIPs from the error
- List<String> eipNames = extractEipNames(error);
+ List<String> eipNames = extractEipNames(error, catalog);
JsonArray eipsJson = new JsonArray();
for (String eip : eipNames) {
EipModel model = catalog.eipModel(eip);
@@ -175,7 +177,7 @@ public class DiagnoseTools {
/**
* Extract component names from endpoint URIs and other patterns in the
error text.
*/
- private List<String> extractComponentNames(String error) {
+ private List<String> extractComponentNames(String error, CamelCatalog
catalog) {
List<String> found = new ArrayList<>();
// Try endpoint URI pattern
@@ -210,7 +212,7 @@ public class DiagnoseTools {
/**
* Extract EIP names from the error text.
*/
- private List<String> extractEipNames(String error) {
+ private List<String> extractEipNames(String error, CamelCatalog catalog) {
List<String> found = new ArrayList<>();
String lowerError = error.toLowerCase();
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/ExplainTools.java
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/ExplainTools.java
index 49e63fc2b345..9fd798a78604 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/ExplainTools.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/ExplainTools.java
@@ -20,12 +20,12 @@ import java.util.ArrayList;
import java.util.List;
import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
import io.quarkiverse.mcp.server.Tool;
import io.quarkiverse.mcp.server.ToolArg;
import io.quarkiverse.mcp.server.ToolCallException;
import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.tooling.model.EipModel;
import org.apache.camel.util.json.JsonArray;
@@ -40,11 +40,8 @@ import org.apache.camel.util.json.JsonObject;
@ApplicationScoped
public class ExplainTools {
- private final CamelCatalog catalog;
-
- public ExplainTools() {
- this.catalog = new DefaultCamelCatalog();
- }
+ @Inject
+ CatalogService catalogService;
/**
* Tool to get enriched context for a Camel route.
@@ -55,13 +52,19 @@ public class ExplainTools {
"Use this context to understand and explain the
route.")
public String camel_route_context(
@ToolArg(description = "The Camel route content (YAML, XML, or
Java DSL)") String route,
- @ToolArg(description = "Route format: yaml, xml, or java (default:
yaml)") String format) {
+ @ToolArg(description = "Route format: yaml, xml, or java (default:
yaml)") String format,
+ @ToolArg(description = "Runtime type: main, spring-boot, or
quarkus (default: main)") String runtime,
+ @ToolArg(description = "Camel version to use (e.g., 4.17.0). If
not specified, uses the default catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion.")
String platformBom) {
if (route == null || route.isBlank()) {
throw new ToolCallException("Route content is required", null);
}
try {
+ CamelCatalog catalog = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
+
String resolvedFormat = format != null && !format.isBlank() ?
format.toLowerCase() : "yaml";
JsonObject result = new JsonObject();
@@ -69,7 +72,7 @@ public class ExplainTools {
result.put("route", route);
// Extract and document components
- List<String> componentNames = extractComponents(route);
+ List<String> componentNames = extractComponents(route, catalog);
JsonArray components = new JsonArray();
for (String comp : componentNames) {
ComponentModel model = catalog.componentModel(comp);
@@ -88,7 +91,7 @@ public class ExplainTools {
result.put("components", components);
// Extract and document EIPs
- List<String> eipNames = extractEips(route);
+ List<String> eipNames = extractEips(route, catalog);
JsonArray eips = new JsonArray();
for (String eip : eipNames) {
EipModel model = catalog.eipModel(eip);
@@ -121,7 +124,7 @@ public class ExplainTools {
/**
* Extract component names from route content.
*/
- private List<String> extractComponents(String route) {
+ private List<String> extractComponents(String route, CamelCatalog catalog)
{
List<String> found = new ArrayList<>();
String lowerRoute = route.toLowerCase();
@@ -137,7 +140,7 @@ public class ExplainTools {
/**
* Extract EIP names from route content.
*/
- private List<String> extractEips(String route) {
+ private List<String> extractEips(String route, CamelCatalog catalog) {
List<String> found = new ArrayList<>();
String lowerRoute = route.toLowerCase();
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/HardenTools.java
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/HardenTools.java
index 6202a5e0e3aa..1686af3ba056 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/HardenTools.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/HardenTools.java
@@ -26,7 +26,6 @@ import io.quarkiverse.mcp.server.Tool;
import io.quarkiverse.mcp.server.ToolArg;
import io.quarkiverse.mcp.server.ToolCallException;
import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.util.json.JsonArray;
import org.apache.camel.util.json.JsonObject;
@@ -41,13 +40,10 @@ import org.apache.camel.util.json.JsonObject;
public class HardenTools {
@Inject
- SecurityData securityData;
-
- private final CamelCatalog catalog;
+ CatalogService catalogService;
- public HardenTools() {
- this.catalog = new DefaultCamelCatalog();
- }
+ @Inject
+ SecurityData securityData;
/**
* Tool to get security hardening context for a Camel route.
@@ -58,13 +54,19 @@ public class HardenTools {
"hardening recommendations for the route.")
public String camel_route_harden_context(
@ToolArg(description = "The Camel route content (YAML, XML, or
Java DSL)") String route,
- @ToolArg(description = "Route format: yaml, xml, or java (default:
yaml)") String format) {
+ @ToolArg(description = "Route format: yaml, xml, or java (default:
yaml)") String format,
+ @ToolArg(description = "Runtime type: main, spring-boot, or
quarkus (default: main)") String runtime,
+ @ToolArg(description = "Camel version to use (e.g., 4.17.0). If
not specified, uses the default catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion.")
String platformBom) {
if (route == null || route.isBlank()) {
throw new ToolCallException("Route content is required", null);
}
try {
+ CamelCatalog catalog = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
+
String resolvedFormat = format != null && !format.isBlank() ?
format.toLowerCase() : "yaml";
JsonObject result = new JsonObject();
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldTools.java
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldTools.java
index 0c0090cb94a0..fbfabb47efa0 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldTools.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldTools.java
@@ -30,7 +30,6 @@ import io.quarkiverse.mcp.server.Tool;
import io.quarkiverse.mcp.server.ToolArg;
import io.quarkiverse.mcp.server.ToolCallException;
import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.util.json.JsonArray;
import org.apache.camel.util.json.JsonObject;
@@ -65,13 +64,10 @@ public class TestScaffoldTools {
private static final Pattern XML_TOD =
Pattern.compile("<toD\\s+uri=[\"']([^\"']+)[\"']", Pattern.CASE_INSENSITIVE);
@Inject
- TestInfraData testInfraData;
-
- private final CamelCatalog catalog;
+ CatalogService catalogService;
- public TestScaffoldTools() {
- this.catalog = new DefaultCamelCatalog();
- }
+ @Inject
+ TestInfraData testInfraData;
/**
* Tool to generate a JUnit 5 test skeleton for a Camel route.
@@ -84,13 +80,26 @@ public class TestScaffoldTools {
public String camel_route_test_scaffold(
@ToolArg(description = "The Camel route definition (YAML or XML)")
String route,
@ToolArg(description = "Route format: yaml or xml (default:
yaml)") String format,
- @ToolArg(description = "Target runtime: main or spring-boot
(default: main)") String runtime) {
+ @ToolArg(description = "Target runtime: main or spring-boot
(default: main)") String runtime,
+ @ToolArg(description = "Camel version to use for catalog lookups
(e.g., 4.17.0). "
+ + "If not specified, uses the default
catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion
for catalog lookups.") String platformBom) {
if (route == null || route.isBlank()) {
throw new ToolCallException("Route content is required", null);
}
try {
+ boolean hasVersion = camelVersion != null &&
!camelVersion.isBlank();
+ boolean hasBom = platformBom != null && !platformBom.isBlank();
+ CamelCatalog catalog;
+ if (hasVersion || hasBom) {
+ catalog = catalogService.loadCatalog(runtime, camelVersion,
platformBom);
+ } else {
+ catalog = catalogService.getDefaultCatalog();
+ }
+
String resolvedFormat = resolveFormat(format);
String resolvedRuntime = resolveRuntime(runtime);
@@ -118,7 +127,7 @@ public class TestScaffoldTools {
: generateMainTest(fromEndpoints, mockEndpoints,
infraServices);
// Build JSON result
- return buildResult(testCode, resolvedFormat, resolvedRuntime,
+ return buildResult(catalog, testCode, resolvedFormat,
resolvedRuntime,
allSchemes, fromEndpoints, toEndpoints, mockEndpoints,
infraServices);
} catch (ToolCallException e) {
@@ -509,7 +518,7 @@ public class TestScaffoldTools {
// ---- JSON result builder ----
private String buildResult(
- String testCode, String format, String runtime,
+ CamelCatalog catalog, String testCode, String format, String
runtime,
List<String> allSchemes, List<String> fromEndpoints, List<String>
toEndpoints,
List<String> mockEndpoints, List<TestInfraData.TestInfraInfo>
infraServices) {
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformTools.java
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformTools.java
index cc24017392cc..f6abeb11d7e5 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformTools.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformTools.java
@@ -27,13 +27,13 @@ import java.util.List;
import java.util.Map;
import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
import com.networknt.schema.ValidationMessage;
import io.quarkiverse.mcp.server.Tool;
import io.quarkiverse.mcp.server.ToolArg;
import io.quarkiverse.mcp.server.ToolCallException;
import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.catalog.EndpointValidationResult;
import org.apache.camel.dsl.yaml.validator.YamlValidator;
import org.apache.camel.impl.DefaultCamelContext;
@@ -50,12 +50,10 @@ import org.apache.camel.xml.in.ModelParser;
@ApplicationScoped
public class TransformTools {
- private final CamelCatalog catalog;
- private YamlValidator yamlValidator;
+ @Inject
+ CatalogService catalogService;
- public TransformTools() {
- this.catalog = new DefaultCamelCatalog(true);
- }
+ private YamlValidator yamlValidator;
/**
* Tool to validate a Camel route or endpoint URI.
@@ -64,13 +62,19 @@ public class TransformTools {
"Checks syntax, required options, and valid parameter
names.")
public ValidationResult camel_validate_route(
@ToolArg(description = "Camel endpoint URI to validate (e.g.,
'kafka:myTopic?brokers=localhost:9092')") String uri,
- @ToolArg(description = "YAML route definition to validate") String
route) {
+ @ToolArg(description = "YAML route definition to validate") String
route,
+ @ToolArg(description = "Runtime type: main, spring-boot, or
quarkus (default: main)") String runtime,
+ @ToolArg(description = "Camel version to use (e.g., 4.17.0). If
not specified, uses the default catalog version.") String camelVersion,
+ @ToolArg(description = "Platform BOM coordinates in GAV format
(groupId:artifactId:version). "
+ + "When provided, overrides camelVersion.")
String platformBom) {
if (uri == null && route == null) {
throw new ToolCallException("Either 'uri' or 'route' is required",
null);
}
try {
+ CamelCatalog catalog = catalogService.loadCatalog(runtime,
camelVersion, platformBom);
+
ValidationResult result = new ValidationResult();
if (uri != null) {
@@ -120,7 +124,7 @@ public class TransformTools {
result.note = "Full route validation requires loading the
route into a CamelContext. " +
"Use 'camel run --validate' for complete
validation.";
- List<String> uris = extractUrisFromRoute(route);
+ List<String> uris = extractUrisFromRoute(route, catalog);
if (!uris.isEmpty()) {
Map<String, Boolean> uriValidations = new HashMap<>();
boolean allValid = true;
@@ -301,7 +305,7 @@ public class TransformTools {
/**
* Extract endpoint URIs from a YAML route definition.
*/
- private List<String> extractUrisFromRoute(String route) {
+ private List<String> extractUrisFromRoute(String route, CamelCatalog
catalog) {
List<String> uris = new ArrayList<>();
String[] lines = route.split("\n");
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogToolsTest.java
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogToolsTest.java
index 38c5d87b4c4f..1face11c0145 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogToolsTest.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogToolsTest.java
@@ -31,8 +31,11 @@ class CatalogToolsTest {
private static final String BUILTIN_VERSION = new
DefaultCamelCatalog().getCatalogVersion();
private CatalogTools createTools(String repos) {
+ CatalogService catalogService = new CatalogService();
+ catalogService.catalogRepos = Optional.ofNullable(repos);
+
CatalogTools tools = new CatalogTools();
- tools.catalogRepos = Optional.ofNullable(repos);
+ tools.catalogService = catalogService;
return tools;
}
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
index a6413338fb0d..de560acfafaf 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
@@ -31,6 +31,9 @@ class DependencyCheckToolsTest {
DependencyCheckToolsTest() {
tools = new DependencyCheckTools();
+ CatalogService catalogService = new CatalogService();
+ catalogService.catalogRepos = java.util.Optional.empty();
+ tools.catalogService = catalogService;
tools.dependencyData = new DependencyData();
}
@@ -140,14 +143,14 @@ class DependencyCheckToolsTest {
@Test
void nullPomThrows() {
- assertThatThrownBy(() -> tools.camel_dependency_check(null, null))
+ assertThatThrownBy(() -> tools.camel_dependency_check(null, null,
null, null, null))
.isInstanceOf(ToolCallException.class)
.hasMessageContaining("required");
}
@Test
void blankPomThrows() {
- assertThatThrownBy(() -> tools.camel_dependency_check(" ", null))
+ assertThatThrownBy(() -> tools.camel_dependency_check(" ", null,
null, null, null))
.isInstanceOf(ToolCallException.class)
.hasMessageContaining("required");
}
@@ -156,7 +159,7 @@ class DependencyCheckToolsTest {
@Test
void resultContainsProjectInfo() throws Exception {
- String json = tools.camel_dependency_check(POM_WITH_BOM, null);
+ String json = tools.camel_dependency_check(POM_WITH_BOM, null, null,
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonObject projectInfo = result.getMap("projectInfo");
@@ -167,7 +170,7 @@ class DependencyCheckToolsTest {
@Test
void detectsSpringBootRuntime() throws Exception {
- String json = tools.camel_dependency_check(POM_SPRING_BOOT, null);
+ String json = tools.camel_dependency_check(POM_SPRING_BOOT, null,
null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonObject projectInfo = result.getMap("projectInfo");
@@ -178,7 +181,7 @@ class DependencyCheckToolsTest {
@Test
void detectsOutdatedVersion() throws Exception {
- String json = tools.camel_dependency_check(POM_WITH_BOM, null);
+ String json = tools.camel_dependency_check(POM_WITH_BOM, null, null,
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonObject versionStatus = result.getMap("versionStatus");
@@ -190,7 +193,7 @@ class DependencyCheckToolsTest {
@Test
void versionStatusContainsCatalogVersion() throws Exception {
- String json = tools.camel_dependency_check(POM_WITH_BOM, null);
+ String json = tools.camel_dependency_check(POM_WITH_BOM, null, null,
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonObject versionStatus = result.getMap("versionStatus");
@@ -204,7 +207,7 @@ class DependencyCheckToolsTest {
// POM without BOM has only camel-core, route uses kafka
String route = "from:\n uri: kafka:myTopic\n steps:\n - to:
log:out";
- String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route);
+ String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route,
null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray missing = (JsonArray) result.get("missingDependencies");
@@ -219,7 +222,7 @@ class DependencyCheckToolsTest {
// POM_WITH_BOM already has camel-kafka
String route = "from:\n uri: kafka:myTopic\n steps:\n - to:
log:out";
- String json = tools.camel_dependency_check(POM_WITH_BOM, route);
+ String json = tools.camel_dependency_check(POM_WITH_BOM, route, null,
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray missing = (JsonArray) result.get("missingDependencies");
@@ -234,7 +237,7 @@ class DependencyCheckToolsTest {
void missingDepContainsSnippet() throws Exception {
String route = "from:\n uri: kafka:myTopic\n steps:\n - to:
log:out";
- String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route);
+ String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route,
null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray missing = (JsonArray) result.get("missingDependencies");
@@ -253,7 +256,7 @@ class DependencyCheckToolsTest {
@Test
void noMissingDepsWithoutRoutes() throws Exception {
- String json = tools.camel_dependency_check(POM_WITH_BOM, null);
+ String json = tools.camel_dependency_check(POM_WITH_BOM, null, null,
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray missing = (JsonArray) result.get("missingDependencies");
@@ -265,7 +268,7 @@ class DependencyCheckToolsTest {
// timer, log, direct are core components - should not be reported as
missing
String route = "from:\n uri: timer:tick\n steps:\n - to: log:out";
- String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route);
+ String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route,
null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray missing = (JsonArray) result.get("missingDependencies");
@@ -279,7 +282,7 @@ class DependencyCheckToolsTest {
@Test
void detectsVersionConflictWithBom() throws Exception {
- String json = tools.camel_dependency_check(POM_WITH_CONFLICT, null);
+ String json = tools.camel_dependency_check(POM_WITH_CONFLICT, null,
null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray conflicts = (JsonArray) result.get("versionConflicts");
@@ -293,7 +296,7 @@ class DependencyCheckToolsTest {
@Test
void noConflictWithPropertyPlaceholderVersion() throws Exception {
// POM_WITH_BOM uses ${camel.version} - not a conflict
- String json = tools.camel_dependency_check(POM_WITH_BOM, null);
+ String json = tools.camel_dependency_check(POM_WITH_BOM, null, null,
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray conflicts = (JsonArray) result.get("versionConflicts");
@@ -303,7 +306,7 @@ class DependencyCheckToolsTest {
@Test
void noConflictWithoutBom() throws Exception {
// No BOM means explicit versions are expected
- String json = tools.camel_dependency_check(POM_WITHOUT_BOM, null);
+ String json = tools.camel_dependency_check(POM_WITHOUT_BOM, null,
null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray conflicts = (JsonArray) result.get("versionConflicts");
@@ -314,7 +317,7 @@ class DependencyCheckToolsTest {
@Test
void recommendsUpgradeWhenOutdated() throws Exception {
- String json = tools.camel_dependency_check(POM_WITH_BOM, null);
+ String json = tools.camel_dependency_check(POM_WITH_BOM, null, null,
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray recommendations = (JsonArray) result.get("recommendations");
@@ -326,7 +329,7 @@ class DependencyCheckToolsTest {
@Test
void recommendsBomWhenMissing() throws Exception {
- String json = tools.camel_dependency_check(POM_WITHOUT_BOM, null);
+ String json = tools.camel_dependency_check(POM_WITHOUT_BOM, null,
null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray recommendations = (JsonArray) result.get("recommendations");
@@ -340,7 +343,7 @@ class DependencyCheckToolsTest {
void recommendsMissingDeps() throws Exception {
String route = "from:\n uri: kafka:myTopic\n steps:\n - to:
log:out";
- String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route);
+ String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route,
null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray recommendations = (JsonArray) result.get("recommendations");
@@ -356,7 +359,7 @@ class DependencyCheckToolsTest {
void summaryShowsHealthyWhenNoIssues() throws Exception {
// Use a pom with current catalog version to avoid outdated flag
// Since we can't easily match the catalog version, we just check
structure
- String json = tools.camel_dependency_check(POM_WITH_BOM, null);
+ String json = tools.camel_dependency_check(POM_WITH_BOM, null, null,
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonObject summary = result.getMap("summary");
@@ -370,7 +373,7 @@ class DependencyCheckToolsTest {
void summaryCountsAllIssues() throws Exception {
String route = "from:\n uri: kafka:myTopic\n steps:\n - to:
log:out";
- String json = tools.camel_dependency_check(POM_WITH_CONFLICT, route);
+ String json = tools.camel_dependency_check(POM_WITH_CONFLICT, route,
null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonObject summary = result.getMap("summary");
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseToolsTest.java
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseToolsTest.java
index be45f4d52942..de85575b93e3 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseToolsTest.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DiagnoseToolsTest.java
@@ -31,6 +31,9 @@ class DiagnoseToolsTest {
DiagnoseToolsTest() {
tools = new DiagnoseTools();
+ CatalogService catalogService = new CatalogService();
+ catalogService.catalogRepos = java.util.Optional.empty();
+ tools.catalogService = catalogService;
tools.diagnoseData = new DiagnoseData();
}
@@ -38,14 +41,14 @@ class DiagnoseToolsTest {
@Test
void nullErrorThrows() {
- assertThatThrownBy(() -> tools.camel_error_diagnose(null))
+ assertThatThrownBy(() -> tools.camel_error_diagnose(null, null, null,
null))
.isInstanceOf(ToolCallException.class)
.hasMessageContaining("required");
}
@Test
void blankErrorThrows() {
- assertThatThrownBy(() -> tools.camel_error_diagnose(" "))
+ assertThatThrownBy(() -> tools.camel_error_diagnose(" ", null, null,
null))
.isInstanceOf(ToolCallException.class)
.hasMessageContaining("required");
}
@@ -56,7 +59,7 @@ class DiagnoseToolsTest {
void identifiesNoSuchEndpointException() throws Exception {
String error = "org.apache.camel.NoSuchEndpointException: No endpoint
could be found for: kafak:myTopic";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
@@ -70,7 +73,7 @@ class DiagnoseToolsTest {
+ "Failed to resolve endpoint:
kafka:myTopic?unknownOption=value due to: "
+ "There are 1 parameters that couldn't be set on the
endpoint.";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
@@ -83,7 +86,7 @@ class DiagnoseToolsTest {
String error = "org.apache.camel.FailedToCreateRouteException: "
+ "Failed to create route route1:
Route(route1)[From[direct:start] -> [To[log:out]]]";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
@@ -97,7 +100,7 @@ class DiagnoseToolsTest {
String error = "org.apache.camel.FailedToCreateRouteException: Failed
to create route\n"
+ "Caused by:
org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
@@ -110,7 +113,7 @@ class DiagnoseToolsTest {
+ "No type converter available to convert from type:
java.lang.String "
+ "to the required type: java.io.InputStream";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
@@ -123,7 +126,7 @@ class DiagnoseToolsTest {
String error = "org.apache.camel.ExchangeTimedOutException: "
+ "The OUT message was not received within: 30000
millis";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
@@ -136,7 +139,7 @@ class DiagnoseToolsTest {
String error =
"org.apache.camel.component.direct.DirectConsumerNotAvailableException: "
+ "No consumers available on endpoint:
direct://myEndpoint";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
@@ -149,7 +152,7 @@ class DiagnoseToolsTest {
String error = "org.apache.camel.PropertyBindingException: "
+ "Error binding property (brokerz=localhost:9092) with
name: brokerz on bean";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
@@ -162,7 +165,7 @@ class DiagnoseToolsTest {
String error = "org.apache.camel.NoSuchBeanException: "
+ "No bean could be found in the registry for:
myProcessor";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
@@ -177,7 +180,7 @@ class DiagnoseToolsTest {
String error = "org.apache.camel.ResolveEndpointFailedException: "
+ "Failed to resolve endpoint:
kafka:myTopic?brokers=localhost:9092";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray components = result.getCollection("identifiedComponents");
@@ -192,7 +195,7 @@ class DiagnoseToolsTest {
void identifiesDirectComponent() throws Exception {
String error = "No consumers available on endpoint: direct://start";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray components = result.getCollection("identifiedComponents");
@@ -208,7 +211,7 @@ class DiagnoseToolsTest {
void resultContainsCommonCauses() throws Exception {
String error = "org.apache.camel.NoSuchEndpointException: No endpoint
could be found for: xyz:test";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
JsonObject first = (JsonObject) exceptions.get(0);
@@ -221,7 +224,7 @@ class DiagnoseToolsTest {
void resultContainsSuggestedFixes() throws Exception {
String error = "org.apache.camel.NoSuchEndpointException: No endpoint
could be found for: xyz:test";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
JsonObject first = (JsonObject) exceptions.get(0);
@@ -234,7 +237,7 @@ class DiagnoseToolsTest {
void resultContainsDocumentationLinks() throws Exception {
String error = "org.apache.camel.NoSuchEndpointException: No endpoint
could be found for: xyz:test";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
JsonObject first = (JsonObject) exceptions.get(0);
@@ -248,7 +251,7 @@ class DiagnoseToolsTest {
void resultContainsSummary() throws Exception {
String error = "org.apache.camel.NoSuchEndpointException: No endpoint";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonObject summary = result.getMap("summary");
@@ -261,7 +264,7 @@ class DiagnoseToolsTest {
void componentDocumentationUrlPresent() throws Exception {
String error = "Failed to resolve endpoint: kafka:myTopic";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray components = result.getCollection("identifiedComponents");
@@ -277,7 +280,7 @@ class DiagnoseToolsTest {
void unrecognizedErrorReturnsDiagnosedFalse() throws Exception {
String error = "Some random error that is not a Camel exception";
- String json = tools.camel_error_diagnose(error);
+ String json = tools.camel_error_diagnose(error, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonObject summary = result.getMap("summary");
@@ -301,7 +304,7 @@ class DiagnoseToolsTest {
\tat
org.apache.camel.component.direct.DirectComponent.createEndpoint(DirectComponent.java:62)
""";
- String json = tools.camel_error_diagnose(stackTrace);
+ String json = tools.camel_error_diagnose(stackTrace, null, null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray exceptions = result.getCollection("identifiedExceptions");
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldToolsTest.java
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldToolsTest.java
index 90705763a219..d561ffacf828 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldToolsTest.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldToolsTest.java
@@ -33,21 +33,28 @@ class TestScaffoldToolsTest {
TestScaffoldToolsTest() {
tools = new TestScaffoldTools();
+ tools.catalogService = createCatalogService();
tools.testInfraData = new TestInfraData();
}
+ private static CatalogService createCatalogService() {
+ CatalogService service = new CatalogService();
+ service.catalogRepos = java.util.Optional.empty();
+ return service;
+ }
+
// ---- Input validation ----
@Test
void nullRouteThrows() {
- assertThatThrownBy(() -> tools.camel_route_test_scaffold(null, null,
null))
+ assertThatThrownBy(() -> tools.camel_route_test_scaffold(null, null,
null, null, null))
.isInstanceOf(ToolCallException.class)
.hasMessageContaining("required");
}
@Test
void blankRouteThrows() {
- assertThatThrownBy(() -> tools.camel_route_test_scaffold(" ",
"yaml", "main"))
+ assertThatThrownBy(() -> tools.camel_route_test_scaffold(" ",
"yaml", "main", null, null))
.isInstanceOf(ToolCallException.class)
.hasMessageContaining("required");
}
@@ -190,7 +197,7 @@ class TestScaffoldToolsTest {
- to: log:done
""";
- String json = tools.camel_route_test_scaffold(route, "yaml", "main");
+ String json = tools.camel_route_test_scaffold(route, "yaml", "main",
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
assertThat(result.getString("runtime")).isEqualTo("main");
@@ -213,7 +220,7 @@ class TestScaffoldToolsTest {
- to: kafka:orders
""";
- String json = tools.camel_route_test_scaffold(route, "yaml", "main");
+ String json = tools.camel_route_test_scaffold(route, "yaml", "main",
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
String testCode = result.getString("testCode");
@@ -232,7 +239,7 @@ class TestScaffoldToolsTest {
- to: log:done
""";
- String json = tools.camel_route_test_scaffold(route, "yaml", "main");
+ String json = tools.camel_route_test_scaffold(route, "yaml", "main",
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray mocks = result.getCollection("mockEndpoints");
@@ -250,7 +257,7 @@ class TestScaffoldToolsTest {
- to: seda:async
""";
- String json = tools.camel_route_test_scaffold(route, "yaml", "main");
+ String json = tools.camel_route_test_scaffold(route, "yaml", "main",
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray mocks = result.getCollection("mockEndpoints");
@@ -269,7 +276,7 @@ class TestScaffoldToolsTest {
- to: kafka:orders
""";
- String json = tools.camel_route_test_scaffold(route, "yaml",
"spring-boot");
+ String json = tools.camel_route_test_scaffold(route, "yaml",
"spring-boot", null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
assertThat(result.getString("runtime")).isEqualTo("spring-boot");
@@ -293,7 +300,7 @@ class TestScaffoldToolsTest {
- to: log:done
""";
- String json = tools.camel_route_test_scaffold(route, "yaml", "main");
+ String json = tools.camel_route_test_scaffold(route, "yaml", "main",
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray infra = result.getCollection("testInfraServices");
@@ -316,7 +323,7 @@ class TestScaffoldToolsTest {
</route>
""";
- String json = tools.camel_route_test_scaffold(route, "xml", "main");
+ String json = tools.camel_route_test_scaffold(route, "xml", "main",
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray infra = result.getCollection("testInfraServices");
@@ -335,7 +342,7 @@ class TestScaffoldToolsTest {
- to: mongodb:myDb?collection=orders
""";
- String json = tools.camel_route_test_scaffold(route, "yaml", "main");
+ String json = tools.camel_route_test_scaffold(route, "yaml", "main",
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray infra = result.getCollection("testInfraServices");
@@ -357,7 +364,7 @@ class TestScaffoldToolsTest {
- to: activemq:queue:output
""";
- String json = tools.camel_route_test_scaffold(route, "yaml", "main");
+ String json = tools.camel_route_test_scaffold(route, "yaml", "main",
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray infra = result.getCollection("testInfraServices");
@@ -378,7 +385,7 @@ class TestScaffoldToolsTest {
- to: kafka:orders
""";
- String json = tools.camel_route_test_scaffold(route, "yaml", "main");
+ String json = tools.camel_route_test_scaffold(route, "yaml", "main",
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray deps = result.getCollection("mavenDependencies");
@@ -398,7 +405,7 @@ class TestScaffoldToolsTest {
- to: kafka:orders
""";
- String json = tools.camel_route_test_scaffold(route, "yaml",
"spring-boot");
+ String json = tools.camel_route_test_scaffold(route, "yaml",
"spring-boot", null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonArray deps = result.getCollection("mavenDependencies");
@@ -421,7 +428,7 @@ class TestScaffoldToolsTest {
- to: log:done
""";
- String json = tools.camel_route_test_scaffold(route, "yaml", "main");
+ String json = tools.camel_route_test_scaffold(route, "yaml", "main",
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
JsonObject summary = result.getMap("summary");
@@ -444,7 +451,7 @@ class TestScaffoldToolsTest {
- to: log:done
""";
- String json = tools.camel_route_test_scaffold(route, null, null);
+ String json = tools.camel_route_test_scaffold(route, null, null, null,
null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
assertThat(result.getString("format")).isEqualTo("yaml");
@@ -463,7 +470,7 @@ class TestScaffoldToolsTest {
</route>
""";
- String json = tools.camel_route_test_scaffold(route, "xml", "main");
+ String json = tools.camel_route_test_scaffold(route, "xml", "main",
null, null);
JsonObject result = (JsonObject) Jsoner.deserialize(json);
String testCode = result.getString("testCode");