diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc index 7327faf481881..12a8f9e2ae8cc 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc @@ -65,6 +65,11 @@ URI) to opt back in to caching during dev mode. ==== camel-jbang-mcp +The `camel_route_context` tool response no longer echoes the input route back to the caller. The +`route` field has been removed from `RouteContextResult`; the response now contains only `format`, +`components`, `eips`, and `summary`. This reduces token consumption for callers passing large +routes. + The `camel_catalog_component_doc` tool no longer returns every component option by default. Two new arguments make the response lean by default to reduce LLM context-window pressure: 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 66d0a027c5168..db3b1bce230f4 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 @@ -89,7 +89,7 @@ public RouteContextResult camel_route_context( RouteContextSummary summary = new RouteContextSummary(components.size(), eips.size()); - return new RouteContextResult(resolvedFormat, route, components, eips, summary); + return new RouteContextResult(resolvedFormat, components, eips, summary); } catch (ToolCallException e) { throw e; } catch (Throwable e) { @@ -155,7 +155,7 @@ private String camelCaseToDash(String text) { // Result records public record RouteContextResult( - String format, String route, List components, + String format, List components, List eips, RouteContextSummary summary) { } diff --git a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/ExplainToolsTest.java b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/ExplainToolsTest.java new file mode 100644 index 0000000000000..dd4d39928e908 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/ExplainToolsTest.java @@ -0,0 +1,89 @@ +/* + * 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.lang.reflect.RecordComponent; +import java.util.Arrays; +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.quarkiverse.mcp.server.ToolCallException; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ExplainToolsTest { + + private ExplainTools createTools() { + CatalogService catalogService = new CatalogService(); + catalogService.catalogRepos = Optional.empty(); + + ExplainTools tools = new ExplainTools(); + tools.catalogService = catalogService; + return tools; + } + + @Test + void resultDoesNotEchoInputRoute() throws Exception { + ExplainTools tools = createTools(); + // Distinctive markers that would only appear if the input was echoed back. + String marker = "MARKER-CAMEL-23474-payload"; + String route = "- route:\n from:\n uri: timer:" + marker + "\n steps:\n" + + " - log: '${body}'\n"; + + ExplainTools.RouteContextResult result + = tools.camel_route_context(route, "yaml", null, null, null); + + // RouteContextResult must not carry a 'route' record component (CAMEL-23474). + assertThat(Arrays.stream(ExplainTools.RouteContextResult.class.getRecordComponents()) + .map(RecordComponent::getName)) + .as("RouteContextResult must not echo input route") + .doesNotContain("route"); + + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + String json = mapper.writeValueAsString(result); + + // Distinctive input markers must not appear in the response payload. + assertThat(json).doesNotContain(marker); + assertThat(json).contains("\"format\":\"yaml\""); + } + + @Test + void blankRouteThrows() { + ExplainTools tools = createTools(); + + assertThatThrownBy(() -> tools.camel_route_context("", "yaml", null, null, null)) + .isInstanceOf(ToolCallException.class) + .hasMessageContaining("Route content is required"); + } + + @Test + void extractsKnownComponent() { + ExplainTools tools = createTools(); + String route = "- route:\n from:\n uri: timer:tick\n steps:\n - log: '${body}'\n"; + + ExplainTools.RouteContextResult result + = tools.camel_route_context(route, "yaml", null, null, null); + + assertThat(result.components()) + .as("timer component should be detected") + .anyMatch(c -> "timer".equals(c.name())); + } +}