This is an automated email from the ASF dual-hosted git repository.
gnodet 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 68b37ecf8db5 Add MCP tools to browse Camel JBang examples
68b37ecf8db5 is described below
commit 68b37ecf8db5a349f8d6a1806850d825c6737c56
Author: Guillaume Nodet <[email protected]>
AuthorDate: Fri May 22 11:50:10 2026 +0200
Add MCP tools to browse Camel JBang examples
- Add camel_catalog_examples tool to list/search examples by name, tag, or
difficulty level
- Add camel_catalog_example_file tool to read file contents from bundled
examples
(returns GitHub URL for non-bundled ones)
- Reuses existing ExampleHelper and camel-jbang-example-catalog.json
- Documents the new tools in camel-jbang-mcp.adoc
- Javadoc with @since 4.21 tags
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
.../modules/ROOT/pages/camel-jbang-mcp.adoc | 37 ++++-
.../dsl/jbang/core/commands/mcp/ExampleTools.java | 166 +++++++++++++++++++++
2 files changed, 202 insertions(+), 1 deletion(-)
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
index a65c413b0d96..4781c437a76c 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
@@ -34,7 +34,7 @@ over the MCP protocol.
== Available Tools
-The server exposes 28 tools organized into eleven functional areas, plus 3
prompts that provide structured
+The server exposes 30 tools organized into twelve functional areas, plus 3
prompts that provide structured
multi-step workflows.
=== Catalog Exploration
@@ -92,6 +92,23 @@ multi-step workflows.
examples, and the Kamelet's Maven dependencies.
|===
+=== Example Catalog
+
+[cols="1,3",options="header"]
+|===
+| Tool | Description
+
+| `camel_catalog_examples`
+| List available Camel JBang examples with filtering by name, description, or
tag (case-insensitive substring match).
+ Supports filtering by difficulty level (`beginner`, `intermediate`,
`advanced`) and limiting the number of results.
+ Returns name, title, description, level, tags, and file list for each
example.
+
+| `camel_catalog_example_file`
+| Get the content of a specific file from a Camel JBang example. For bundled
examples, returns the file content
+ directly. For non-bundled examples, returns a GitHub URL where the file can
be found. Use `camel_catalog_examples`
+ first to discover example names and their files.
+|===
+
=== Route Understanding
[cols="1,3",options="header"]
@@ -468,6 +485,24 @@ What options does the aws-s3-source kamelet accept?
The assistant calls `camel_catalog_kamelet_doc` with `kamelet=aws-s3-source`
and returns the complete property
list including required fields, types, defaults, and Maven dependencies.
+=== Browsing Examples
+
+----
+Show me beginner-level Camel examples related to REST
+----
+
+The assistant calls `camel_catalog_examples` with `filter=rest` and
`level=beginner` and returns matching examples
+with their name, title, description, difficulty level, tags, and file list.
+
+To read a specific file from an example:
+
+----
+Show me the route file from the rest-api example
+----
+
+The assistant calls `camel_catalog_example_file` with `example=rest-api` and
`file=route.camel.yaml` and returns
+the file content directly for bundled examples, or a GitHub URL for
non-bundled ones.
+
=== Validating an Endpoint URI
----
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/ExampleTools.java
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/ExampleTools.java
new file mode 100644
index 000000000000..02c8fc78f7af
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/ExampleTools.java
@@ -0,0 +1,166 @@
+/*
+ * 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.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+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.common.ExampleHelper;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.json.JsonObject;
+
+/**
+ * MCP Tools for browsing Camel JBang examples from the
+ * <a
href="https://github.com/apache/camel-jbang-examples">camel-jbang-examples</a>
repository.
+ * <p>
+ * Provides two tools:
+ * <ul>
+ * <li>{@code camel_catalog_examples} — list and filter available examples by
name, tag, or difficulty level</li>
+ * <li>{@code camel_catalog_example_file} — read the content of a specific
file from a bundled example, or get a GitHub
+ * URL for non-bundled ones</li>
+ * </ul>
+ * Uses {@link ExampleHelper} for catalog loading, filtering, and file
retrieval.
+ *
+ * @since 4.21
+ */
+@ApplicationScoped
+public class ExampleTools {
+
+ @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint
= false, openWorldHint = false),
+ description = "List available Camel JBang examples. "
+ + "Returns name, title, description, difficulty level,
and tags. "
+ + "Use filter to search by name, description, or tag. "
+ + "Use level to filter by difficulty (beginner,
intermediate, advanced).")
+ public ExampleListResult camel_catalog_examples(
+ @ToolArg(description = "Filter examples by name, description, or
tag (case-insensitive substring match)") String filter,
+ @ToolArg(description = "Filter by difficulty level: beginner,
intermediate, or advanced") String level,
+ @ToolArg(description = "Maximum number of results to return
(default: 50)") Integer limit) {
+
+ int maxResults = limit != null ? limit : 50;
+
+ try {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ List<JsonObject> filtered = ExampleHelper.filterExamples(catalog,
filter);
+
+ List<ExampleInfo> result = new ArrayList<>();
+ for (JsonObject entry : filtered) {
+ if (level != null && !level.isBlank()) {
+ String entryLevel = entry.getString("level");
+ if (entryLevel == null ||
!entryLevel.equalsIgnoreCase(level)) {
+ continue;
+ }
+ }
+
+ result.add(toExampleInfo(entry));
+
+ if (result.size() >= maxResults) {
+ break;
+ }
+ }
+
+ return new ExampleListResult(result.size(), result);
+ } catch (Throwable e) {
+ throw new ToolCallException(
+ "Failed to list examples (" + e.getClass().getName() + "):
" + e.getMessage(), null);
+ }
+ }
+
+ @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint
= false, openWorldHint = false),
+ description = "Get the content of a specific file from a Camel JBang
example. "
+ + "Use camel_catalog_examples first to find the
example name and its files. "
+ + "Only bundled examples can return file contents
directly; "
+ + "for non-bundled examples, a GitHub URL is returned
instead.")
+ public ExampleFileResult camel_catalog_example_file(
+ @ToolArg(description = "Example name (e.g., timer-log, rest-api,
circuit-breaker)") String example,
+ @ToolArg(description = "File name within the example (e.g.,
route.camel.yaml, application.properties)") String file) {
+
+ if (example == null || example.isBlank()) {
+ throw new ToolCallException("Example name is required", null);
+ }
+ if (file == null || file.isBlank()) {
+ throw new ToolCallException("File name is required", null);
+ }
+
+ try {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ JsonObject entry = ExampleHelper.findExample(catalog, example);
+ if (entry == null) {
+ throw new ToolCallException("Example not found: " + example,
null);
+ }
+
+ List<String> files = ExampleHelper.getFiles(entry);
+ if (!files.contains(file)) {
+ throw new ToolCallException(
+ "File '" + file + "' not found in example '" + example
+ + "'. Available files: " + files,
+ null);
+ }
+
+ if (ExampleHelper.isBundled(entry)) {
+ String resourcePath = "examples/" + example + "/" + file;
+ try (InputStream is =
ExampleHelper.class.getClassLoader().getResourceAsStream(resourcePath)) {
+ if (is != null) {
+ String content = IOHelper.loadText(is);
+ return new ExampleFileResult(example, file, content,
null);
+ }
+ }
+ throw new ToolCallException("Could not read bundled file: " +
resourcePath, null);
+ } else {
+ String githubUrl = ExampleHelper.getGithubUrl(entry) + "/" +
file;
+ return new ExampleFileResult(example, file, null, githubUrl);
+ }
+ } catch (ToolCallException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new ToolCallException(
+ "Failed to read example file (" + e.getClass().getName() +
"): " + e.getMessage(), null);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private ExampleInfo toExampleInfo(JsonObject entry) {
+ Collection<String> tags = (Collection<String>) entry.get("tags");
+ return new ExampleInfo(
+ entry.getString("name"),
+ entry.getString("title"),
+ entry.getString("description"),
+ entry.getString("level"),
+ tags != null ? new ArrayList<>(tags) : List.of(),
+ ExampleHelper.isBundled(entry),
+ ExampleHelper.requiresDocker(entry),
+ ExampleHelper.getFiles(entry));
+ }
+
+ // Result records
+
+ public record ExampleListResult(int count, List<ExampleInfo> examples) {
+ }
+
+ public record ExampleInfo(String name, String title, String description,
String level,
+ List<String> tags, boolean bundled, boolean requiresDocker,
List<String> files) {
+ }
+
+ public record ExampleFileResult(String example, String file, String
content, String githubUrl) {
+ }
+}