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

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 9797b13445fb57b40150cb2a297a76c60a4a832c
Author: James Netherton <[email protected]>
AuthorDate: Tue Nov 10 14:29:30 2026 +0000

    CAMEL-26469: camel-mcp-server, camel-openai - use mcp-json-jackson3
    
    mcp-json-jackson2 needs json-schema-validator 2, which cannot share a
    classpath with the json-schema-validator 3 of camel-json-validator and the
    YAML DSL validator, so the MCP JSON module moves with CAMEL-23172.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 components/camel-ai/camel-mcp-server-api/pom.xml         | 10 +++++++++-
 components/camel-ai/camel-mcp-server/pom.xml             | 16 +++++++++++++---
 .../component/mcp/server/main/McpServerIconsParser.java  | 11 ++++++-----
 .../component/mcp/server/stdio/StdioMcpServerEngine.java |  2 +-
 .../mcp/server/main/McpServerIconsParserTest.java        |  7 +++++++
 .../component/mcp/server/stdio/McpStdioHarnessMain.java  |  2 +-
 components/camel-ai/camel-openai/pom.xml                 |  9 ++++++++-
 .../modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc  | 16 ++++++++++++++++
 dsl/camel-jbang/camel-jbang-core/pom.xml                 |  2 +-
 9 files changed, 62 insertions(+), 13 deletions(-)

diff --git a/components/camel-ai/camel-mcp-server-api/pom.xml 
b/components/camel-ai/camel-mcp-server-api/pom.xml
index d93771b61ebd..63616fa88dc6 100644
--- a/components/camel-ai/camel-mcp-server-api/pom.xml
+++ b/components/camel-ai/camel-mcp-server-api/pom.xml
@@ -70,10 +70,18 @@
         </dependency>
         <dependency>
             <groupId>io.modelcontextprotocol.sdk</groupId>
-            <artifactId>mcp-json-jackson2</artifactId>
+            <artifactId>mcp-json-jackson3</artifactId>
             <version>${mcp-java-sdk-version}</version>
             <scope>test</scope>
         </dependency>
+        <!-- direct dependency so camel-json-validator and the MCP SDK get the 
same json-schema-validator:
+             mcp-json-jackson3 declares an older one, which would otherwise 
win by nearest-wins -->
+        <dependency>
+            <groupId>com.networknt</groupId>
+            <artifactId>json-schema-validator</artifactId>
+            <version>${networknt-json-schema-validator-version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.awaitility</groupId>
             <artifactId>awaitility</artifactId>
diff --git a/components/camel-ai/camel-mcp-server/pom.xml 
b/components/camel-ai/camel-mcp-server/pom.xml
index b5ffdf6bc8d7..b80aa4f3f3e8 100644
--- a/components/camel-ai/camel-mcp-server/pom.xml
+++ b/components/camel-ai/camel-mcp-server/pom.xml
@@ -64,13 +64,23 @@
         </dependency>
         <dependency>
             <groupId>io.modelcontextprotocol.sdk</groupId>
-            <artifactId>mcp-json-jackson2</artifactId>
+            <artifactId>mcp-json-jackson3</artifactId>
             <version>${mcp-java-sdk-version}</version>
         </dependency>
+        <!-- direct dependency so camel-json-validator and the MCP SDK get the 
same json-schema-validator:
+             mcp-json-jackson3 declares an older one, which would otherwise 
win by nearest-wins -->
+        <dependency>
+            <groupId>com.networknt</groupId>
+            <artifactId>json-schema-validator</artifactId>
+            <version>${networknt-json-schema-validator-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>tools.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
         <!-- direct dependency so consumers resolving without the jackson-bom 
get a
              jackson-annotations aligned with jackson-databind: mcp-core 
declares an older
-             jackson-annotations that otherwise wins by nearest-wins and 
breaks databind
-             at runtime (NoClassDefFoundError: JsonSerializeAs) -->
+             jackson-annotations, which could otherwise win by nearest-wins -->
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-annotations</artifactId>
diff --git 
a/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/main/McpServerIconsParser.java
 
b/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/main/McpServerIconsParser.java
index 51622f504d75..f8f37a833587 100644
--- 
a/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/main/McpServerIconsParser.java
+++ 
b/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/main/McpServerIconsParser.java
@@ -19,16 +19,17 @@ package org.apache.camel.component.mcp.server.main;
 import java.util.ArrayList;
 import java.util.List;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.camel.component.mcp.server.McpServerIcon;
+import tools.jackson.databind.JsonNode;
+import tools.jackson.databind.ObjectMapper;
+import tools.jackson.databind.json.JsonMapper;
 
 /**
  * Parses {@code camel.server.mcp-server-icons} JSON into {@link 
McpServerIcon} instances.
  */
 final class McpServerIconsParser {
 
-    private static final ObjectMapper MAPPER = new ObjectMapper();
+    private static final ObjectMapper MAPPER = 
JsonMapper.builderWithJackson2Defaults().build();
 
     private McpServerIconsParser() {
     }
@@ -67,7 +68,7 @@ final class McpServerIconsParser {
 
     private static String text(JsonNode node, String field) {
         JsonNode value = node.get(field);
-        return value != null && !value.isNull() ? value.asText() : null;
+        return value != null && !value.isNull() ? value.asString("") : null;
     }
 
     private static List<String> stringList(JsonNode node) {
@@ -79,7 +80,7 @@ final class McpServerIconsParser {
         }
         List<String> values = new ArrayList<>(node.size());
         for (JsonNode item : node) {
-            values.add(item.asText());
+            values.add(item.asString(""));
         }
         return List.copyOf(values);
     }
diff --git 
a/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngine.java
 
b/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngine.java
index 75ee636aab66..5723501eca37 100644
--- 
a/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngine.java
+++ 
b/components/camel-ai/camel-mcp-server/src/main/java/org/apache/camel/component/mcp/server/stdio/StdioMcpServerEngine.java
@@ -101,7 +101,7 @@ public class StdioMcpServerEngine extends ServiceSupport 
implements McpServerEng
      * The JSON mapper of the MCP SDK, found with the service loader of the 
application class loader of Camel.
      * <p>
      * The MCP SDK finds the mapper with the thread context class loader. 
Camel CLI (camel run) loads the downloaded
-     * jars, such as mcp-json-jackson2, in the application context class 
loader of CamelContext, which is the thread
+     * jars, such as mcp-json-jackson3, in the application context class 
loader of CamelContext, which is the thread
      * context class loader while CamelContext starts, but not while Camel 
Main configures it and initializes this
      * engine.
      */
diff --git 
a/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/McpServerIconsParserTest.java
 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/McpServerIconsParserTest.java
index 7e765df01c2f..f4aa2036ba6b 100644
--- 
a/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/McpServerIconsParserTest.java
+++ 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/main/McpServerIconsParserTest.java
@@ -92,4 +92,11 @@ class McpServerIconsParserTest {
                 .isInstanceOf(IllegalArgumentException.class)
                 .hasMessageContaining("src");
     }
+
+    @Test
+    void testRejectsObjectAsSrc() {
+        assertThatThrownBy(() -> 
McpServerIconsParser.parse("[{\"src\":{\"url\":\"https://example.com/icon.png\"}}]";))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessage("Each MCP server icon must include a non-blank 
src");
+    }
 }
diff --git 
a/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/McpStdioHarnessMain.java
 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/McpStdioHarnessMain.java
index 39b621ffe8e0..9eedfa15f574 100644
--- 
a/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/McpStdioHarnessMain.java
+++ 
b/components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/stdio/McpStdioHarnessMain.java
@@ -39,7 +39,7 @@ public final class McpStdioHarnessMain {
         String tags = args.length > 0 ? args[0] : "harness";
         if (args.length > 1 && "cli".equals(args[1])) {
             // as Camel CLI (camel run): the thread context class loader 
cannot see the downloaded jars, such as
-            // mcp-json-jackson2, while Camel Main configures the CamelContext
+            // mcp-json-jackson3, while Camel Main configures the CamelContext
             Thread.currentThread().setContextClassLoader(new 
HidingClassLoader(McpStdioHarnessMain.class.getClassLoader()));
         }
 
diff --git a/components/camel-ai/camel-openai/pom.xml 
b/components/camel-ai/camel-openai/pom.xml
index 6dc5b8a00500..cf8531b6e39c 100644
--- a/components/camel-ai/camel-openai/pom.xml
+++ b/components/camel-ai/camel-openai/pom.xml
@@ -55,9 +55,16 @@
         </dependency>
         <dependency>
             <groupId>io.modelcontextprotocol.sdk</groupId>
-            <artifactId>mcp-json-jackson2</artifactId>
+            <artifactId>mcp-json-jackson3</artifactId>
             <version>${mcp-java-sdk-version}</version>
         </dependency>
+        <!-- direct dependency so camel-json-validator and the MCP SDK get the 
same json-schema-validator:
+             mcp-json-jackson3 declares an older one, which would otherwise 
win by nearest-wins -->
+        <dependency>
+            <groupId>com.networknt</groupId>
+            <artifactId>json-schema-validator</artifactId>
+            <version>${networknt-json-schema-validator-version}</version>
+        </dependency>
 
         <dependency>
             <groupId>org.apache.camel</groupId>
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 0e100cd80924..ed754ebdef93 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -8275,6 +8275,22 @@ that uses or replaces it must move to Jackson 3.
 follows YAML 1.2: `yes`, `no`, `on` and `off` are no longer booleans, and 
`1_000` and `0b101` are no longer numbers. An
 option such as `streaming: yes` is now reported; the route fails to start with 
it, so write `true` or `false`.
 
+=== Jackson 3 - the MCP JSON module of camel-mcp-server and camel-openai
+
+`camel-mcp-server` and the MCP client of `camel-openai` now use the Jackson 3 
JSON module of the MCP Java SDK,
+`io.modelcontextprotocol.sdk:mcp-json-jackson3`, instead of 
`mcp-json-jackson2`. `McpJsonDefaults.getMapper()` returns a
+mapper of that module. `camel-mcp-server` no longer brings 
`com.fasterxml.jackson.core:jackson-databind` to your application;
+`camel-openai` still does, for the OpenAI client.
+
+The module moved with `camel-json-validator`: `mcp-json-jackson2` needs 
json-schema-validator 2, and `mcp-json-jackson3` and
+`camel-json-validator` need json-schema-validator 3, which an application can 
only have one of. If your application declares
+`mcp-json-jackson2` itself, replace it with `mcp-json-jackson3`: with 
json-schema-validator 3, `mcp-json-jackson2` fails with a
+`NoSuchMethodError` as soon as it checks the schema of a tool.
+
+The mapper of `mcp-json-jackson3` uses the Jackson 3 defaults (see the table 
in <<_jackson_3,Jackson 3>>). This mainly
+changes the JSON of the SDK's own message records and of POJOs inside a `Map`, 
such as the order of their properties and
+how enums and dates are written.
+
 === camel-docling - the apiKeyHeader option is deprecated
 
 The `apiKeyHeader` option never had any effect: the name of the header that 
carries the API key is fixed in the
diff --git a/dsl/camel-jbang/camel-jbang-core/pom.xml 
b/dsl/camel-jbang/camel-jbang-core/pom.xml
index 07521e16e20e..4abbe5b9a029 100644
--- a/dsl/camel-jbang/camel-jbang-core/pom.xml
+++ b/dsl/camel-jbang/camel-jbang-core/pom.xml
@@ -276,7 +276,7 @@
         </dependency>
         <dependency>
             <groupId>io.modelcontextprotocol.sdk</groupId>
-            <artifactId>mcp-json-jackson2</artifactId>
+            <artifactId>mcp-json-jackson3</artifactId>
             <version>${mcp-java-sdk-version}</version>
             <scope>test</scope>
         </dependency>

Reply via email to