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

acosentino 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 88d6605ac977 CAMEL-22886 - Camel-Jbang: Add an MCP module (#20976)
88d6605ac977 is described below

commit 88d6605ac977d34d84bb81106dbf152e801e799e
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Jan 22 13:36:33 2026 +0100

    CAMEL-22886 - Camel-Jbang: Add an MCP module (#20976)
    
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 .../jbang/core/commands/version/VersionList.java   |  34 +-
 dsl/camel-jbang/camel-jbang-mcp/pom.xml            | 133 +++++++
 .../dsl/jbang/core/commands/mcp/CatalogTools.java  | 404 +++++++++++++++++++++
 .../dsl/jbang/core/commands/mcp/ExplainTools.java  | 167 +++++++++
 .../jbang/core/commands/mcp/TransformTools.java    | 216 +++++++++++
 .../dsl/jbang/core/commands/mcp/VersionTools.java  | 122 +++++++
 .../src/main/resources/application.properties      |  31 ++
 dsl/camel-jbang/pom.xml                            |   1 +
 8 files changed, 1091 insertions(+), 17 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionList.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionList.java
index ed595831bca1..e5a60eee2523 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionList.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionList.java
@@ -83,65 +83,65 @@ public class VersionList extends CamelCommand {
                         completionCandidates = 
RuntimeCompletionCandidates.class,
                         converter = RuntimeTypeConverter.class,
                         description = "Runtime (${COMPLETION-CANDIDATES})")
-    RuntimeType runtime = RuntimeType.main;
+    public RuntimeType runtime = RuntimeType.main;
 
     @CommandLine.Option(names = { "--from-version" },
                         description = "Filter by Camel version (inclusive). 
Will start from 4.0 if no version ranges provided.")
-    String fromVersion;
+    public String fromVersion;
 
     @CommandLine.Option(names = { "--to-version" },
                         description = "Filter by Camel version (exclusive)")
-    String toVersion;
+    public String toVersion;
 
     @CommandLine.Option(names = { "--from-date" },
                         description = "Filter by release date (inclusive)")
-    String fromDate;
+    public String fromDate;
 
     @CommandLine.Option(names = { "--to-date" },
                         description = "Filter by release date (exclusive)")
-    String toDate;
+    public String toDate;
 
     @CommandLine.Option(names = { "--sort" },
                         description = "Sort by (version, date, or days)", 
defaultValue = "version")
-    String sort;
+    public String sort;
 
     @CommandLine.Option(names = { "--repo", "--repos" },
                         description = "Additional maven repositories (Use 
commas to separate multiple repositories)")
-    String repositories;
+    public String repositories;
 
     @CommandLine.Option(names = { "--lts" }, description = "Only show LTS 
supported releases", defaultValue = "false")
-    boolean lts;
+    public boolean lts;
 
     @CommandLine.Option(names = { "--eol" }, description = "Include releases 
that are end-of-life", defaultValue = "true")
-    boolean eol = true;
+    public boolean eol = true;
 
     @CommandLine.Option(names = { "--patch" }, description = "Whether to 
include patch releases (x.y.z)", defaultValue = "true")
-    boolean patch = true;
+    public boolean patch = true;
 
     @CommandLine.Option(names = { "--rc" }, description = "Include also 
milestone or RC releases", defaultValue = "false")
-    boolean rc;
+    public boolean rc;
 
     @CommandLine.Option(names = { "--days" }, description = "Whether to 
include days since release", defaultValue = "true")
-    boolean days;
+    public boolean days;
 
     @CommandLine.Option(names = { "--date-format" }, description = "The format 
to show the date (such as dd-MM-yyyy)",
                         defaultValue = DEFAULT_DATE_FORMAT)
-    String dateFormat;
+    public String dateFormat;
 
     @CommandLine.Option(names = { "--tail" },
                         description = "The number of lines from the end of the 
table to show.")
-    int tail;
+    public int tail;
 
     @CommandLine.Option(names = { "--fresh" }, description = "Make sure we use 
fresh (i.e. non-cached) resources",
                         defaultValue = "false")
-    boolean fresh;
+    public boolean fresh;
 
     @CommandLine.Option(names = { "--download" }, defaultValue = "true",
                         description = "Whether to allow automatic downloading 
JAR dependencies (over the internet)")
-    boolean download = true;
+    public boolean download = true;
 
     @CommandLine.Option(names = { "--json" }, description = "Output in JSON 
Format", defaultValue = "false")
-    boolean jsonOutput;
+    public boolean jsonOutput;
 
     public VersionList(CamelJBangMain main) {
         super(main);
diff --git a/dsl/camel-jbang/camel-jbang-mcp/pom.xml 
b/dsl/camel-jbang/camel-jbang-mcp/pom.xml
new file mode 100644
index 000000000000..052ad258c5ee
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-mcp/pom.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-jbang-parent</artifactId>
+        <version>4.18.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>camel-jbang-mcp</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Camel :: JBang :: MCP</name>
+    <description>Camel JBang MCP (Model Context Protocol) Server</description>
+
+    <properties>
+        <firstVersion>4.18.0</firstVersion>
+        <label>jbang,mcp,ai</label>
+        <supportLevel>Preview</supportLevel>
+        <camel-prepare-component>false</camel-prepare-component>
+        <quarkus-mcp-server-version>1.2.0</quarkus-mcp-server-version>
+        <quarkus.platform.version>3.30.6</quarkus.platform.version>
+        <!-- Build uber-jar for easy JBang execution -->
+        <quarkus.package.jar.type>uber-jar</quarkus.package.jar.type>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>io.quarkus.platform</groupId>
+                <artifactId>quarkus-bom</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- Quarkus MCP Server with STDIO transport -->
+        <dependency>
+            <groupId>io.quarkiverse.mcp</groupId>
+            <artifactId>quarkus-mcp-server-stdio</artifactId>
+            <version>${quarkus-mcp-server-version}</version>
+        </dependency>
+
+        <!-- Quarkus core dependencies -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-arc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-jackson</artifactId>
+        </dependency>
+
+        <!-- Camel JBang Core for catalog and utilities -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-jbang-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>io.quarkus.platform</groupId>
+                <artifactId>quarkus-maven-plugin</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>build</goal>
+                            <goal>generate-code</goal>
+                            <goal>generate-code-tests</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <parameters>true</parameters>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <systemPropertyVariables>
+                        
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
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
new file mode 100644
index 000000000000..29f813da9014
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/CatalogTools.java
@@ -0,0 +1,404 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+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;
+
+/**
+ * MCP Tools for querying the Camel Catalog using Quarkus MCP Server.
+ */
+@ApplicationScoped
+public class CatalogTools {
+
+    private CamelCatalog catalog;
+
+    public CatalogTools() {
+        this.catalog = new DefaultCamelCatalog(true);
+    }
+
+    /**
+     * Tool to list available Camel components.
+     */
+    @Tool(description = "List available Camel components from the catalog. " +
+                        "Returns component name, description, and labels. " +
+                        "Use filter to search by name, label to filter by 
category.")
+    public ComponentListResult camel_catalog_components(
+            @ToolArg(description = "Filter components by name 
(case-insensitive substring match)") String filter,
+            @ToolArg(description = "Filter by category label (e.g., cloud, 
messaging, database, file)") String label,
+            @ToolArg(description = "Maximum number of results to return 
(default: 50)") Integer limit,
+            @ToolArg(description = "Runtime type: main, spring-boot, or 
quarkus (default: main)") String runtime,
+            @ToolArg(description = "Specific Camel version to query (e.g., 
4.4.0). If not specified, uses the default catalog version.") String 
camelVersion) {
+
+        int maxResults = limit != null ? limit : 50;
+
+        try {
+            CamelCatalog cat = loadCatalog(runtime, camelVersion);
+
+            List<ComponentInfo> components = findComponentNames(cat).stream()
+                    .map(cat::componentModel)
+                    .filter(m -> m != null)
+                    .filter(m -> matchesFilter(m.getScheme(), m.getTitle(), 
m.getDescription(), filter))
+                    .filter(m -> matchesLabel(m.getLabel(), label))
+                    .limit(maxResults)
+                    .map(this::toComponentInfo)
+                    .collect(Collectors.toList());
+
+            return new ComponentListResult(components.size(), 
cat.getCatalogVersion(), components);
+        } catch (Exception e) {
+            throw new ToolCallException("Failed to list components: " + 
e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Tool to get detailed documentation for a specific component.
+     */
+    @Tool(description = "Get detailed documentation for a Camel component 
including all options, " +
+                        "endpoint parameters, and usage examples.")
+    public ComponentDetailResult camel_catalog_component_doc(
+            @ToolArg(description = "Component name (e.g., kafka, http, file, 
timer)") String component,
+            @ToolArg(description = "Runtime type: main, spring-boot, or 
quarkus (default: main)") String runtime,
+            @ToolArg(description = "Specific Camel version to query (e.g., 
4.4.0). If not specified, uses the default catalog version.") String 
camelVersion) {
+
+        if (component == null || component.isBlank()) {
+            throw new ToolCallException("Component name is required", null);
+        }
+
+        try {
+            CamelCatalog cat = loadCatalog(runtime, camelVersion);
+            ComponentModel model = cat.componentModel(component);
+            if (model == null) {
+                throw new ToolCallException("Component not found: " + 
component, null);
+            }
+
+            return toComponentDetailResult(model);
+        } catch (ToolCallException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ToolCallException("Failed to get component doc: " + 
e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Tool to list data formats.
+     */
+    @Tool(description = "List available Camel data formats for 
marshalling/unmarshalling " +
+                        "(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) {
+
+        int maxResults = limit != null ? limit : 50;
+
+        try {
+            List<DataFormatInfo> dataFormats = 
catalog.findDataFormatNames().stream()
+                    .map(catalog::dataFormatModel)
+                    .filter(m -> m != null)
+                    .filter(m -> matchesFilter(m.getName(), m.getTitle(), 
m.getDescription(), filter))
+                    .limit(maxResults)
+                    .map(this::toDataFormatInfo)
+                    .collect(Collectors.toList());
+
+            return new DataFormatListResult(dataFormats.size(), dataFormats);
+        } catch (Exception e) {
+            throw new ToolCallException("Failed to list data formats: " + 
e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Tool to list expression languages.
+     */
+    @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) {
+
+        try {
+            List<LanguageInfo> languages = catalog.findLanguageNames().stream()
+                    .map(catalog::languageModel)
+                    .filter(m -> m != null)
+                    .filter(m -> matchesFilter(m.getName(), m.getTitle(), 
m.getDescription(), filter))
+                    .map(this::toLanguageInfo)
+                    .collect(Collectors.toList());
+
+            return new LanguageListResult(languages.size(), languages);
+        } catch (Exception e) {
+            throw new ToolCallException("Failed to list languages: " + 
e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Tool to list EIPs (Enterprise Integration Patterns).
+     */
+    @Tool(description = "List Camel Enterprise Integration Patterns (EIPs) 
like split, aggregate, " +
+                        "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) {
+
+        try {
+            List<EipInfo> eips = catalog.findModelNames().stream()
+                    .map(catalog::eipModel)
+                    .filter(m -> m != null)
+                    .filter(m -> matchesFilter(m.getName(), m.getTitle(), 
m.getDescription(), filter))
+                    .filter(m -> matchesLabel(m.getLabel(), label))
+                    .map(this::toEipInfo)
+                    .collect(Collectors.toList());
+
+            return new EipListResult(eips.size(), eips);
+        } catch (Exception e) {
+            throw new ToolCallException("Failed to list EIPs: " + 
e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Tool to get detailed documentation for a specific EIP.
+     */
+    @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) {
+
+        if (eip == null || eip.isBlank()) {
+            throw new ToolCallException("EIP name is required", null);
+        }
+
+        EipModel model = catalog.eipModel(eip);
+        if (model == null) {
+            throw new ToolCallException("EIP not found: " + eip, null);
+        }
+
+        return toEipDetailResult(model);
+    }
+
+    // Catalog loading
+
+    private CamelCatalog loadCatalog(String runtime, String camelVersion) 
throws Exception {
+        // If a specific version is requested, load that version's catalog
+        if (camelVersion != null && !camelVersion.isBlank()) {
+            RuntimeType runtimeType = resolveRuntime(runtime);
+            if (runtimeType == RuntimeType.springBoot) {
+                return CatalogLoader.loadSpringBootCatalog(null, camelVersion, 
true);
+            } else if (runtimeType == RuntimeType.quarkus) {
+                return CatalogLoader.loadQuarkusCatalog(null, camelVersion, 
null, true);
+            } else {
+                return CatalogLoader.loadCatalog(null, camelVersion, true);
+            }
+        }
+
+        // No specific version, use runtime-specific catalog or default
+        if (runtime == null || runtime.isBlank() || 
"main".equalsIgnoreCase(runtime)) {
+            return catalog;
+        }
+
+        RuntimeType runtimeType = RuntimeType.fromValue(runtime);
+        if (runtimeType == RuntimeType.springBoot) {
+            return CatalogLoader.loadSpringBootCatalog(null, null, true);
+        } else if (runtimeType == RuntimeType.quarkus) {
+            return CatalogLoader.loadQuarkusCatalog(null, 
RuntimeType.QUARKUS_VERSION, null, true);
+        }
+
+        return catalog;
+    }
+
+    private RuntimeType resolveRuntime(String runtime) {
+        if (runtime == null || runtime.isBlank() || 
"main".equalsIgnoreCase(runtime)) {
+            return RuntimeType.main;
+        }
+        return RuntimeType.fromValue(runtime);
+    }
+
+    private static List<String> findComponentNames(CamelCatalog catalog) {
+        List<String> answer = catalog.findComponentNames();
+        List<String> copy = new ArrayList<>(answer);
+        copy.removeIf(String::isBlank);
+        return copy;
+    }
+
+    // Helper methods
+
+    private boolean matchesFilter(String name, String title, String 
description, String filter) {
+        if (filter == null || filter.isBlank()) {
+            return true;
+        }
+        String lowerFilter = filter.toLowerCase();
+        return (name != null && name.toLowerCase().contains(lowerFilter))
+                || (title != null && title.toLowerCase().contains(lowerFilter))
+                || (description != null && 
description.toLowerCase().contains(lowerFilter));
+    }
+
+    private boolean matchesLabel(String labels, String labelFilter) {
+        if (labelFilter == null || labelFilter.isBlank()) {
+            return true;
+        }
+        if (labels == null) {
+            return false;
+        }
+        return labels.toLowerCase().contains(labelFilter.toLowerCase());
+    }
+
+    // Mapping methods
+
+    private ComponentInfo toComponentInfo(ComponentModel model) {
+        return new ComponentInfo(
+                model.getScheme(),
+                model.getTitle(),
+                model.getDescription(),
+                model.getLabel(),
+                model.isDeprecated(),
+                model.getSupportLevel() != null ? 
model.getSupportLevel().name() : null);
+    }
+
+    private ComponentDetailResult toComponentDetailResult(ComponentModel 
model) {
+        List<OptionInfo> componentOptions = new ArrayList<>();
+        if (model.getComponentOptions() != null) {
+            model.getComponentOptions().forEach(opt -> 
componentOptions.add(new OptionInfo(
+                    opt.getName(),
+                    opt.getDescription(),
+                    opt.getType(),
+                    opt.isRequired(),
+                    opt.getDefaultValue() != null ? 
opt.getDefaultValue().toString() : null,
+                    null)));
+        }
+
+        List<OptionInfo> endpointOptions = new ArrayList<>();
+        if (model.getEndpointOptions() != null) {
+            model.getEndpointOptions().forEach(opt -> endpointOptions.add(new 
OptionInfo(
+                    opt.getName(),
+                    opt.getDescription(),
+                    opt.getType(),
+                    opt.isRequired(),
+                    opt.getDefaultValue() != null ? 
opt.getDefaultValue().toString() : null,
+                    opt.getGroup())));
+        }
+
+        return new ComponentDetailResult(
+                model.getScheme(),
+                model.getTitle(),
+                model.getDescription(),
+                model.getLabel(),
+                model.isDeprecated(),
+                model.getSupportLevel() != null ? 
model.getSupportLevel().name() : null,
+                model.getGroupId(),
+                model.getArtifactId(),
+                model.getVersion(),
+                model.getSyntax(),
+                model.isAsync(),
+                model.isConsumerOnly(),
+                model.isProducerOnly(),
+                componentOptions,
+                endpointOptions);
+    }
+
+    private DataFormatInfo toDataFormatInfo(DataFormatModel model) {
+        return new DataFormatInfo(
+                model.getName(),
+                model.getTitle(),
+                model.getDescription(),
+                model.isDeprecated());
+    }
+
+    private LanguageInfo toLanguageInfo(LanguageModel model) {
+        return new LanguageInfo(
+                model.getName(),
+                model.getTitle(),
+                model.getDescription());
+    }
+
+    private EipInfo toEipInfo(EipModel model) {
+        return new EipInfo(
+                model.getName(),
+                model.getTitle(),
+                model.getDescription(),
+                model.getLabel());
+    }
+
+    private EipDetailResult toEipDetailResult(EipModel model) {
+        List<OptionInfo> options = new ArrayList<>();
+        if (model.getOptions() != null) {
+            model.getOptions().forEach(opt -> options.add(new OptionInfo(
+                    opt.getName(),
+                    opt.getDescription(),
+                    opt.getType(),
+                    opt.isRequired(),
+                    opt.getDefaultValue() != null ? 
opt.getDefaultValue().toString() : null,
+                    null)));
+        }
+
+        return new EipDetailResult(
+                model.getName(),
+                model.getTitle(),
+                model.getDescription(),
+                model.getLabel(),
+                options);
+    }
+
+    // Result record classes for Jackson serialization
+
+    public record ComponentListResult(int count, String camelVersion, 
List<ComponentInfo> components) {
+    }
+
+    public record ComponentInfo(String name, String title, String description, 
String label,
+            boolean deprecated, String supportLevel) {
+    }
+
+    public record ComponentDetailResult(String name, String title, String 
description, String label,
+            boolean deprecated, String supportLevel, String groupId, String 
artifactId,
+            String version, String syntax, boolean async, boolean 
consumerOnly, boolean producerOnly,
+            List<OptionInfo> componentOptions, List<OptionInfo> 
endpointOptions) {
+    }
+
+    public record OptionInfo(String name, String description, String type, 
boolean required,
+            String defaultValue, String group) {
+    }
+
+    public record DataFormatListResult(int count, List<DataFormatInfo> 
dataFormats) {
+    }
+
+    public record DataFormatInfo(String name, String title, String 
description, boolean deprecated) {
+    }
+
+    public record LanguageListResult(int count, List<LanguageInfo> languages) {
+    }
+
+    public record LanguageInfo(String name, String title, String description) {
+    }
+
+    public record EipListResult(int count, List<EipInfo> eips) {
+    }
+
+    public record EipInfo(String name, String title, String description, 
String label) {
+    }
+
+    public record EipDetailResult(String name, String title, String 
description, String label,
+            List<OptionInfo> options) {
+    }
+}
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
new file mode 100644
index 000000000000..6e24a7da9d13
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/ExplainTools.java
@@ -0,0 +1,167 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+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;
+import org.apache.camel.util.json.JsonObject;
+
+/**
+ * MCP Tool for providing enriched context about Camel routes.
+ * <p>
+ * This tool extracts components and EIPs used in a route and returns their 
documentation from the Camel Catalog. The
+ * calling LLM can use this context to formulate its own explanation of the 
route.
+ */
+@ApplicationScoped
+public class ExplainTools {
+
+    private final CamelCatalog catalog;
+
+    public ExplainTools() {
+        this.catalog = new DefaultCamelCatalog();
+    }
+
+    /**
+     * Tool to get enriched context for a Camel route.
+     */
+    @Tool(description = "Get enriched context for a Camel route including 
documentation for all components and EIPs used. "
+                        +
+                        "Returns structured data with component descriptions, 
EIP explanations, and route structure. " +
+                        "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) {
+
+        if (route == null || route.isBlank()) {
+            throw new ToolCallException("Route content is required", null);
+        }
+
+        String resolvedFormat = format != null && !format.isBlank() ? 
format.toLowerCase() : "yaml";
+
+        JsonObject result = new JsonObject();
+        result.put("format", resolvedFormat);
+        result.put("route", route);
+
+        // Extract and document components
+        List<String> componentNames = extractComponents(route);
+        JsonArray components = new JsonArray();
+        for (String comp : componentNames) {
+            ComponentModel model = catalog.componentModel(comp);
+            if (model != null) {
+                JsonObject compJson = new JsonObject();
+                compJson.put("name", comp);
+                compJson.put("title", model.getTitle());
+                compJson.put("description", model.getDescription());
+                compJson.put("label", model.getLabel());
+                compJson.put("syntax", model.getSyntax());
+                compJson.put("producerOnly", model.isProducerOnly());
+                compJson.put("consumerOnly", model.isConsumerOnly());
+                components.add(compJson);
+            }
+        }
+        result.put("components", components);
+
+        // Extract and document EIPs
+        List<String> eipNames = extractEips(route);
+        JsonArray eips = new JsonArray();
+        for (String eip : eipNames) {
+            EipModel model = catalog.eipModel(eip);
+            if (model != null) {
+                JsonObject eipJson = new JsonObject();
+                eipJson.put("name", eip);
+                eipJson.put("title", model.getTitle());
+                eipJson.put("description", model.getDescription());
+                eipJson.put("label", model.getLabel());
+                eips.add(eipJson);
+            }
+        }
+        result.put("eips", eips);
+
+        // Add summary counts
+        JsonObject summary = new JsonObject();
+        summary.put("componentCount", components.size());
+        summary.put("eipCount", eips.size());
+        result.put("summary", summary);
+
+        return result.toJson();
+    }
+
+    /**
+     * Extract component names from route content.
+     */
+    private List<String> extractComponents(String route) {
+        List<String> found = new ArrayList<>();
+        String lowerRoute = route.toLowerCase();
+
+        for (String comp : catalog.findComponentNames()) {
+            if (containsComponent(lowerRoute, comp)) {
+                found.add(comp);
+            }
+        }
+
+        return found;
+    }
+
+    /**
+     * Extract EIP names from route content.
+     */
+    private List<String> extractEips(String route) {
+        List<String> found = new ArrayList<>();
+        String lowerRoute = route.toLowerCase();
+
+        for (String eip : catalog.findModelNames()) {
+            EipModel model = catalog.eipModel(eip);
+            if (model != null) {
+                String eipLower = eip.toLowerCase();
+                String eipDash = camelCaseToDash(eip);
+                if (lowerRoute.contains(eipLower) || 
lowerRoute.contains(eipDash)) {
+                    found.add(eip);
+                }
+            }
+        }
+
+        return found;
+    }
+
+    private boolean containsComponent(String content, String comp) {
+        return content.contains(comp + ":")
+                || content.contains("\"" + comp + "\"")
+                || content.contains("'" + comp + "'");
+    }
+
+    private String camelCaseToDash(String text) {
+        StringBuilder sb = new StringBuilder();
+        for (char c : text.toCharArray()) {
+            if (Character.isUpperCase(c) && sb.length() > 0) {
+                sb.append('-');
+            }
+            sb.append(Character.toLowerCase(c));
+        }
+        return sb.toString();
+    }
+}
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
new file mode 100644
index 000000000000..19ee6a216386
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformTools.java
@@ -0,0 +1,216 @@
+/*
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+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;
+
+/**
+ * MCP Tools for validating and transforming Camel routes using Quarkus MCP 
Server.
+ */
+@ApplicationScoped
+public class TransformTools {
+
+    private final CamelCatalog catalog;
+
+    public TransformTools() {
+        this.catalog = new DefaultCamelCatalog(true);
+    }
+
+    /**
+     * Tool to validate a Camel route or endpoint URI.
+     */
+    @Tool(description = "Validate a Camel endpoint URI or route definition. " +
+                        "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) {
+
+        if (uri == null && route == null) {
+            throw new ToolCallException("Either 'uri' or 'route' is required", 
null);
+        }
+
+        ValidationResult result = new ValidationResult();
+
+        if (uri != null) {
+            result.uri = uri;
+            EndpointValidationResult validation = 
catalog.validateEndpointProperties(uri);
+            result.valid = validation.isSuccess();
+
+            if (!validation.isSuccess()) {
+                ValidationErrors errors = new ValidationErrors();
+                if (validation.getUnknown() != null && 
!validation.getUnknown().isEmpty()) {
+                    errors.unknownOptions = String.join(", ", 
validation.getUnknown());
+                }
+                if (validation.getRequired() != null && 
!validation.getRequired().isEmpty()) {
+                    errors.missingRequired = String.join(", ", 
validation.getRequired());
+                }
+                if (validation.getInvalidEnum() != null && 
!validation.getInvalidEnum().isEmpty()) {
+                    errors.invalidEnumValues = 
validation.getInvalidEnum().toString();
+                }
+                if (validation.getInvalidInteger() != null && 
!validation.getInvalidInteger().isEmpty()) {
+                    errors.invalidIntegers = 
validation.getInvalidInteger().toString();
+                }
+                if (validation.getInvalidBoolean() != null && 
!validation.getInvalidBoolean().isEmpty()) {
+                    errors.invalidBooleans = 
validation.getInvalidBoolean().toString();
+                }
+                if (validation.getSyntaxError() != null) {
+                    errors.syntaxError = validation.getSyntaxError();
+                }
+                result.errors = errors;
+
+                if (validation.getUnknown() != null && 
validation.getUnknownSuggestions() != null) {
+                    Map<String, String> suggestions = new HashMap<>();
+                    for (String unknown : validation.getUnknown()) {
+                        String[] suggestionArr = 
validation.getUnknownSuggestions().get(unknown);
+                        if (suggestionArr != null && suggestionArr.length > 0) 
{
+                            suggestions.put(unknown, String.join(", ", 
suggestionArr));
+                        }
+                    }
+                    if (!suggestions.isEmpty()) {
+                        result.suggestions = suggestions;
+                    }
+                }
+            }
+        }
+
+        if (route != null) {
+            result.routeProvided = true;
+            result.note = "Full route validation requires loading the route 
into a CamelContext. " +
+                          "Use 'camel run --validate' for complete 
validation.";
+
+            List<String> uris = extractUrisFromRoute(route);
+            if (!uris.isEmpty()) {
+                Map<String, Boolean> uriValidations = new HashMap<>();
+                boolean allValid = true;
+                for (String extractedUri : uris) {
+                    EndpointValidationResult validation = 
catalog.validateEndpointProperties(extractedUri);
+                    uriValidations.put(extractedUri, validation.isSuccess());
+                    if (!validation.isSuccess()) {
+                        allValid = false;
+                    }
+                }
+                result.uriValidations = uriValidations;
+                result.valid = allValid;
+            } else {
+                result.valid = true;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Tool to transform routes between DSL formats.
+     */
+    @Tool(description = "Transform a Camel route between different DSL formats 
(YAML, XML). " +
+                        "Note: Java to YAML/XML transformation has 
limitations.")
+    public TransformResult camel_transform_route(
+            @ToolArg(description = "Route definition to transform") String 
route,
+            @ToolArg(description = "Source format (yaml, xml, java)") String 
fromFormat,
+            @ToolArg(description = "Target format (yaml, xml)") String 
toFormat) {
+
+        if (route == null || fromFormat == null || toFormat == null) {
+            throw new ToolCallException("route, fromFormat, and toFormat are 
required", null);
+        }
+
+        TransformResult result = new TransformResult();
+        result.fromFormat = fromFormat;
+        result.toFormat = toFormat;
+        result.note = "Route transformation requires the full Camel route 
parser. " +
+                      "Use 'camel transform route' CLI command for complete 
transformation.";
+        result.supported = true;
+        result.recommendation = "Use 'camel transform route --format=" + 
toFormat + " <file>' for DSL transformation";
+
+        return result;
+    }
+
+    /**
+     * Extract endpoint URIs from a YAML route definition.
+     */
+    private List<String> extractUrisFromRoute(String route) {
+        List<String> uris = new ArrayList<>();
+
+        String[] lines = route.split("\n");
+        for (String line : lines) {
+            line = line.trim();
+            if (line.contains(":") && !line.startsWith("#")) {
+                int colonPos = line.indexOf(":");
+                if (colonPos > 0 && colonPos < line.length() - 1) {
+                    String key = line.substring(0, colonPos).trim();
+                    String value = line.substring(colonPos + 1).trim();
+
+                    if (value.startsWith("\"") && value.endsWith("\"")) {
+                        value = value.substring(1, value.length() - 1);
+                    } else if (value.startsWith("'") && value.endsWith("'")) {
+                        value = value.substring(1, value.length() - 1);
+                    }
+
+                    if ((key.equals("uri") || key.equals("from") || 
key.equals("to"))
+                            && value.contains(":") && !value.startsWith("$")) {
+                        String scheme = value.split(":")[0];
+                        if (catalog.findComponentNames().contains(scheme)) {
+                            uris.add(value);
+                        }
+                    }
+                }
+            }
+        }
+
+        return uris;
+    }
+
+    // Result classes for Jackson serialization
+
+    public static class ValidationResult {
+        public String uri;
+        public boolean valid;
+        public boolean routeProvided;
+        public String note;
+        public ValidationErrors errors;
+        public Map<String, String> suggestions;
+        public Map<String, Boolean> uriValidations;
+    }
+
+    public static class ValidationErrors {
+        public String unknownOptions;
+        public String missingRequired;
+        public String invalidEnumValues;
+        public String invalidIntegers;
+        public String invalidBooleans;
+        public String syntaxError;
+    }
+
+    public static class TransformResult {
+        public String fromFormat;
+        public String toFormat;
+        public String note;
+        public boolean supported;
+        public String recommendation;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/VersionTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/VersionTools.java
new file mode 100644
index 000000000000..eaced47c0bba
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/VersionTools.java
@@ -0,0 +1,122 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+import io.quarkiverse.mcp.server.Tool;
+import io.quarkiverse.mcp.server.ToolArg;
+import io.quarkiverse.mcp.server.ToolCallException;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.commands.version.VersionList;
+import org.apache.camel.dsl.jbang.core.common.RuntimeType;
+import org.apache.camel.dsl.jbang.core.common.StringPrinter;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+
+/**
+ * MCP Tools for querying available Camel versions using Quarkus MCP Server.
+ */
+@ApplicationScoped
+public class VersionTools {
+
+    /**
+     * Tool to list available Camel versions for a specific runtime.
+     */
+    @Tool(description = "List available Camel versions for a specific runtime 
(main, spring-boot, quarkus). " +
+                        "Returns version information including release date, 
JDK requirements, and LTS status.")
+    public VersionListResult camel_version_list(
+            @ToolArg(description = "Runtime type: main, spring-boot, or 
quarkus (default: main)") String runtime,
+            @ToolArg(description = "Only show LTS (Long Term Support) releases 
(default: false)") Boolean lts,
+            @ToolArg(description = "Minimum Camel version to include (e.g., 
4.0)") String fromVersion,
+            @ToolArg(description = "Maximum number of versions to return 
(default: 10)") Integer limit) {
+
+        try {
+            StringPrinter printer = new StringPrinter();
+            CamelJBangMain main = new CamelJBangMain().withPrinter(printer);
+
+            VersionList versionList = new VersionList(main);
+            versionList.runtime = resolveRuntime(runtime);
+            versionList.jsonOutput = true;
+            versionList.lts = lts != null && lts;
+            versionList.fromVersion = fromVersion != null ? fromVersion : 
"4.0";
+            versionList.sort = "-version"; // newest first
+            versionList.download = true;
+            versionList.fresh = false;
+
+            int exitCode = versionList.doCall();
+            if (exitCode != 0) {
+                throw new ToolCallException("Failed to list versions, exit 
code: " + exitCode, null);
+            }
+
+            String jsonOutput = printer.getOutput();
+            JsonArray versions = (JsonArray) Jsoner.deserialize(jsonOutput);
+
+            List<VersionInfo> versionInfos = new ArrayList<>();
+            int maxResults = limit != null ? limit : 10;
+            int count = 0;
+
+            for (Object obj : versions) {
+                if (count >= maxResults) {
+                    break;
+                }
+                JsonObject v = (JsonObject) obj;
+                versionInfos.add(new VersionInfo(
+                        v.getString("camelVersion"),
+                        v.getString("runtime"),
+                        v.getString("runtimeVersion"),
+                        v.getString("jdkVersion"),
+                        v.getString("kind"),
+                        v.getString("releaseDate"),
+                        v.getString("eolDate")));
+                count++;
+            }
+
+            return new VersionListResult(versionInfos.size(), versionInfos);
+        } catch (ToolCallException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ToolCallException("Failed to list versions: " + 
e.getMessage(), e);
+        }
+    }
+
+    private RuntimeType resolveRuntime(String runtime) {
+        if (runtime == null || runtime.isBlank() || 
"main".equalsIgnoreCase(runtime)) {
+            return RuntimeType.main;
+        }
+        return RuntimeType.fromValue(runtime);
+    }
+
+    // Result classes for Jackson serialization
+
+    public record VersionListResult(int count, List<VersionInfo> versions) {
+    }
+
+    public record VersionInfo(
+            String camelVersion,
+            String runtime,
+            String runtimeVersion,
+            String jdkVersion,
+            String kind,
+            String releaseDate,
+            String eolDate) {
+    }
+}
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
new file mode 100644
index 000000000000..16a9e79de4f8
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-mcp/src/main/resources/application.properties
@@ -0,0 +1,31 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+# Camel MCP Server Configuration
+# This file configures the Quarkus MCP Server for Apache Camel
+
+# Server name and version for MCP protocol
+quarkus.application.name=camel-mcp-server
+quarkus.application.version=${camel.version:4.18.0-SNAPSHOT}
+
+# Disable banner for cleaner stdio output
+quarkus.banner.enabled=false
+
+# Logging configuration - stderr only to avoid interfering with stdio transport
+quarkus.log.console.stderr=true
+quarkus.log.level=WARN
+quarkus.log.category."org.apache.camel".level=INFO
+quarkus.log.category."io.quarkiverse.mcp".level=INFO
diff --git a/dsl/camel-jbang/pom.xml b/dsl/camel-jbang/pom.xml
index 539a068e2295..20b03bf31eef 100644
--- a/dsl/camel-jbang/pom.xml
+++ b/dsl/camel-jbang/pom.xml
@@ -37,6 +37,7 @@
         <module>camel-jbang-console</module>
         <module>camel-jbang-core</module>
         <module>camel-jbang-main</module>
+        <module>camel-jbang-mcp</module>
         <module>camel-jbang-plugin-generate</module>
         <module>camel-jbang-plugin-edit</module>
         <module>camel-jbang-plugin-kubernetes</module>

Reply via email to