This is an automated email from the ASF dual-hosted git repository.

oscerd 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 7d7ee401c936 CAMEL-23476: camel-jbang-mcp - strip null fields from MCP 
JSON responses (#23169)
7d7ee401c936 is described below

commit 7d7ee401c936eee7d5c9876bf451d3486de6fa6a
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed May 13 09:10:21 2026 +0200

    CAMEL-23476: camel-jbang-mcp - strip null fields from MCP JSON responses 
(#23169)
    
    Configure the Quarkus Jackson ObjectMapper for camel-jbang-mcp with
    quarkus.jackson.serialization-inclusion=non-null so @Tool result records
    serialized via JsonTextContentEncoder no longer emit fields with null 
values.
    
    JsonTextContentEncoder (the default content encoder used by the MCP server
    for arbitrary objects returned from @Tool methods) injects the default
    Quarkus ObjectMapper. Quarkus' ConfigurationCustomizer reads
    JacksonBuildTimeConfig.serializationInclusion() and applies it to that
    ObjectMapper, so the single property line is enough to strip nulls from
    every result the MCP server emits.
    
    The framework's existing McpObjectMapperCustomizer adds a @JsonInclude
    mixin only to MCP envelope types (ToolResponse, PromptResponse, etc.),
    not to application-level result records like ComponentDetailResult or
    OptionInfo, which is the bug filed in the JIRA.
    
    Reduces LLM context-window pressure for every caller of the camel-jbang-mcp
    server (31 tools, 6 resources, 3 prompts).
    
    Adds McpJsonSerializationTest covering the property setting and the
    omission of null fields from ComponentInfo, OptionInfo and
    ComponentDetailResult.
    
    Closes #23169
---
 .../src/main/resources/application.properties      |   4 +
 .../commands/mcp/McpJsonSerializationTest.java     | 106 +++++++++++++++++++++
 2 files changed, 110 insertions(+)

diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/resources/application.properties 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/resources/application.properties
index 07f949764854..8aa02aefe4b5 100644
--- a/dsl/camel-jbang/camel-jbang-mcp/src/main/resources/application.properties
+++ b/dsl/camel-jbang/camel-jbang-mcp/src/main/resources/application.properties
@@ -32,3 +32,7 @@ quarkus.log.category."io.quarkiverse.mcp".level=INFO
 
 # Disable HTTP server - use STDIO transport only for CLI integration
 quarkus.http.host-enabled=false
+
+# Strip null fields from JSON responses to reduce LLM token consumption.
+# Applies to @Tool result records serialized via JsonTextContentEncoder.
+quarkus.jackson.serialization-inclusion=non-null
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/McpJsonSerializationTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/McpJsonSerializationTest.java
new file mode 100644
index 000000000000..066504a2ca2c
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/McpJsonSerializationTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Verifies the MCP server is configured to strip {@code null} fields from 
JSON responses. The Quarkus MCP framework
+ * serializes {@code @Tool} results through the default {@link ObjectMapper} 
produced by quarkus-jackson, which honors
+ * the {@code quarkus.jackson.serialization-inclusion} build-time property. 
CAMEL-23476.
+ */
+class McpJsonSerializationTest {
+
+    @Test
+    void applicationPropertiesEnforcesNonNullSerialization() throws 
IOException {
+        Properties props = new Properties();
+        try (InputStream in = 
getClass().getResourceAsStream("/application.properties")) {
+            assertThat(in).as("application.properties on 
classpath").isNotNull();
+            props.load(in);
+        }
+        
assertThat(props.getProperty("quarkus.jackson.serialization-inclusion"))
+                .as("quarkus.jackson.serialization-inclusion must be 
'non-null' to drop null fields from MCP responses")
+                .isEqualToIgnoringCase("non-null");
+    }
+
+    @Test
+    void componentInfoNullFieldsAreOmittedFromJson() throws Exception {
+        ObjectMapper mapper = newConfiguredObjectMapper();
+
+        CatalogTools.ComponentInfo info = new CatalogTools.ComponentInfo(
+                "timer", "Timer", null, null, false, null);
+
+        String json = mapper.writeValueAsString(info);
+
+        assertThat(json).contains("\"name\":\"timer\"");
+        assertThat(json).contains("\"title\":\"Timer\"");
+        assertThat(json).contains("\"deprecated\":false");
+        assertThat(json).doesNotContain("\"description\"");
+        assertThat(json).doesNotContain("\"label\"");
+        assertThat(json).doesNotContain("\"supportLevel\"");
+    }
+
+    @Test
+    void optionInfoNullGroupIsOmittedFromJson() throws Exception {
+        ObjectMapper mapper = newConfiguredObjectMapper();
+
+        // Matches the bug from CAMEL-23476: component options pass null for 
group while endpoint options populate it.
+        CatalogTools.OptionInfo opt = new CatalogTools.OptionInfo(
+                "bridgeErrorHandler", "Allows bridging the consumer", 
"boolean", false, "false", null);
+
+        String json = mapper.writeValueAsString(opt);
+
+        assertThat(json).contains("\"name\":\"bridgeErrorHandler\"");
+        assertThat(json).contains("\"required\":false");
+        assertThat(json).doesNotContain("\"group\"");
+    }
+
+    @Test
+    void componentDetailNullCollectionsAreOmittedFromJson() throws Exception {
+        ObjectMapper mapper = newConfiguredObjectMapper();
+
+        CatalogTools.ComponentDetailResult detail = new 
CatalogTools.ComponentDetailResult(
+                "timer", "Timer", null, null, false, null, null, null, null, 
null,
+                false, false, false, null, null);
+
+        String json = mapper.writeValueAsString(detail);
+
+        assertThat(json).contains("\"name\":\"timer\"");
+        assertThat(json).contains("\"async\":false");
+        assertThat(json).doesNotContain("\"description\"");
+        assertThat(json).doesNotContain("\"supportLevel\"");
+        assertThat(json).doesNotContain("\"groupId\"");
+        assertThat(json).doesNotContain("\"componentOptions\"");
+        assertThat(json).doesNotContain("\"endpointOptions\"");
+    }
+
+    private static ObjectMapper newConfiguredObjectMapper() {
+        ObjectMapper mapper = new ObjectMapper();
+        // Mirrors the configuration applied by Quarkus' 
ConfigurationCustomizer when
+        // quarkus.jackson.serialization-inclusion=non-null is set in 
application.properties.
+        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+        return mapper;
+    }
+}

Reply via email to