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>
