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

davsclaus pushed a commit to branch feature/CAMEL-24658-catalog-term-lookup
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 44de46832b6d4961d0d10242db8c1efda1b5aa23
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Sep 8 19:17:19 2026 +0200

    CAMEL-24658: camel-catalog - find components by protocol or product name 
(s3 -> aws2-s3, mqtt -> paho-mqtt5)
    
    Adds suggestComponentNames, suggestDataFormatNames and suggestLanguageNames
    to CamelCatalog. They rank matches by exact scheme, declared aliases,
    alternative schemes, title words, scheme words and finally substring, and
    return a component that shares one implementation under several schemes
    (imap, pop3, smtp) only once. The lookup is a default method working on
    the existing JSON, so it also serves older catalogs loaded via 
CatalogLoader.
    
    Component aliases reuse @Metadata(aliases) on the endpoint class, which the
    endpoint schema generator now copies into the component model. The mail
    component declares mail/email and ActiveMQ declares amq.
    
    The Camel TUI drops its hardcoded scheme alias map in favour of the catalog
    and tui_catalog_doc returns suggestions when a name is not found. The
    camel catalog commands include the suggestions in their did-you-mean output.
    
    Co-Authored-By: Claude Fable 5.1 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../apache/camel/catalog/components/activemq.json  |   1 +
 .../apache/camel/catalog/components/activemq6.json |   1 +
 .../org/apache/camel/catalog/components/imap.json  |   1 +
 .../org/apache/camel/catalog/components/imaps.json |   1 +
 .../org/apache/camel/catalog/components/pop3.json  |   1 +
 .../org/apache/camel/catalog/components/pop3s.json |   1 +
 .../org/apache/camel/catalog/components/smtp.json  |   1 +
 .../org/apache/camel/catalog/components/smtps.json |   1 +
 .../org/apache/camel/catalog/CamelCatalog.java     |  45 +++++
 .../apache/camel/catalog/CatalogTermMatcher.java   | 199 +++++++++++++++++++++
 .../org/apache/camel/catalog/CamelCatalogTest.java |  59 ++++++
 .../apache/camel/component/activemq/activemq.json  |   1 +
 .../camel/component/activemq/ActiveMQEndpoint.java |   2 +
 .../camel/component/activemq6/activemq6.json       |   1 +
 .../component/activemq6/ActiveMQEndpoint.java      |   2 +
 .../org/apache/camel/component/mail/imap.json      |   1 +
 .../org/apache/camel/component/mail/imaps.json     |   1 +
 .../org/apache/camel/component/mail/pop3.json      |   1 +
 .../org/apache/camel/component/mail/pop3s.json     |   1 +
 .../org/apache/camel/component/mail/smtp.json      |   1 +
 .../org/apache/camel/component/mail/smtps.json     |   1 +
 .../apache/camel/component/mail/MailEndpoint.java  |   2 +
 .../modules/ROOT/pages/camel-catalog.adoc          |  34 ++++
 .../core/commands/catalog/CatalogBaseCommand.java  |  14 +-
 .../core/commands/catalog/CatalogComponent.java    |   5 +
 .../core/commands/catalog/CatalogDataFormat.java   |   5 +
 .../core/commands/catalog/CatalogLanguage.java     |   5 +
 .../commands/catalog/CatalogComponentTest.java     |  11 ++
 .../jbang/core/commands/tui/TuiToolRegistry.java   |  69 +++----
 .../tui/TuiToolRegistrySuggestComponentsTest.java  |  46 -----
 .../packaging/EndpointSchemaGeneratorMojo.java     |   6 +
 31 files changed, 429 insertions(+), 91 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json
index a09d44a724dd..d6d6c9c9d608 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json
@@ -9,6 +9,7 @@
     "label": "messaging",
     "javaType": "org.apache.camel.component.activemq.ActiveMQComponent",
     "supportLevel": "Stable",
+    "aliases": [ "amq" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-activemq",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json
index 04ddf3b3a40b..b1e04a0ba8b4 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json
@@ -9,6 +9,7 @@
     "label": "messaging",
     "javaType": "org.apache.camel.component.activemq6.ActiveMQComponent",
     "supportLevel": "Stable",
+    "aliases": [ "amq" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-activemq6",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imap.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imap.json
index 6c49e15393b5..621ba167453a 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imap.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imap.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imaps.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imaps.json
index 2d59fa225fa5..be87ed8c11ae 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imaps.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imaps.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3.json
index 6827a27d73bf..e868fcf6abdd 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3s.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3s.json
index 582c2818dc8a..d2e95187a967 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3s.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3s.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtp.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtp.json
index c0cd0d423a4e..33dff5f867f5 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtp.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtp.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtps.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtps.json
index c5c1721f6c6a..2b2188d35521 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtps.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtps.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java
 
b/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java
index d71a426840bc..c0ba6aa5638f 100644
--- 
a/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java
+++ 
b/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java
@@ -266,6 +266,51 @@ public interface CamelCatalog {
      */
     List<String> findOtherNames(String filter);
 
+    /**
+     * Finds component names that match a term such as a protocol or product 
name, best match first: the exact scheme,
+     * then declared aliases, alternative schemes, words of the title, words 
of the scheme, and finally schemes
+     * containing the term. Components that share one implementation under 
several schemes (such as imap, pop3 and smtp)
+     * are returned once, by the scheme that matched or otherwise their 
primary scheme.
+     * <p>
+     * For example <tt>mqtt</tt> finds <tt>paho-mqtt5</tt>, <tt>s3</tt> finds 
<tt>aws2-s3</tt>, and <tt>amq</tt> finds
+     * <tt>activemq</tt>.
+     *
+     * @param  term the protocol, product, or component name to look for
+     * @param  max  the maximum number of names to return, or 0 for no limit
+     * @return      the matching component names, or an empty list if none 
match
+     * @since       4.23
+     */
+    default List<String> suggestComponentNames(String term, int max) {
+        return CatalogTermMatcher.suggestComponentNames(this, term, max);
+    }
+
+    /**
+     * Finds data format names that match a term such as a format or library 
name (for example <tt>yaml</tt> finds
+     * <tt>snakeYaml</tt>), best match first: the exact name, then declared 
aliases, words of the title, words of the
+     * name, and finally names containing the term.
+     *
+     * @param  term the format, library, or data format name to look for
+     * @param  max  the maximum number of names to return, or 0 for no limit
+     * @return      the matching data format names, or an empty list if none 
match
+     * @since       4.23
+     */
+    default List<String> suggestDataFormatNames(String term, int max) {
+        return CatalogTermMatcher.suggestNames(findDataFormatNames(), 
this::dataFormatModel, term, max);
+    }
+
+    /**
+     * Finds language names that match a term, best match first: the exact 
name, then declared aliases, words of the
+     * title, words of the name, and finally names containing the term.
+     *
+     * @param  term the language name to look for
+     * @param  max  the maximum number of names to return, or 0 for no limit
+     * @return      the matching language names, or an empty list if none match
+     * @since       4.23
+     */
+    default List<String> suggestLanguageNames(String term, int max) {
+        return CatalogTermMatcher.suggestNames(findLanguageNames(), 
this::languageModel, term, max);
+    }
+
     /**
      * Returns the component information as JSON format.
      *
diff --git 
a/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CatalogTermMatcher.java
 
b/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CatalogTermMatcher.java
new file mode 100644
index 000000000000..70093f5e1fa9
--- /dev/null
+++ 
b/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CatalogTermMatcher.java
@@ -0,0 +1,199 @@
+/*
+ * 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.catalog;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.apache.camel.tooling.model.BaseModel;
+import org.apache.camel.tooling.model.ComponentModel;
+
+/**
+ * Ranks catalog entries against a search term such as a protocol or product 
name (mqtt, s3, rabbitmq) for the suggest
+ * methods on {@link CamelCatalog}.
+ */
+final class CatalogTermMatcher {
+
+    private static final int NO_MATCH = -1;
+    /** Shorter terms are too ambiguous to match as a substring of a name. */
+    private static final int MIN_SUBSTRING_LENGTH = 3;
+
+    private record Match(String name, int rank, String group) {
+    }
+
+    private CatalogTermMatcher() {
+    }
+
+    /**
+     * Component names matching the term, best match first. Components that 
share one implementation under several
+     * schemes (imap, pop3, smtp) are returned once: by the scheme that 
matched, or otherwise their primary scheme.
+     */
+    static List<String> suggestComponentNames(CamelCatalog catalog, String 
term, int max) {
+        String key = normalize(term);
+        if (key.isEmpty()) {
+            return List.of();
+        }
+        List<Match> matches = new ArrayList<>();
+        for (String name : catalog.findComponentNames()) {
+            ComponentModel model = name == null || name.isBlank() ? null : 
catalog.componentModel(name);
+            if (model == null) {
+                continue;
+            }
+            int rank = rankComponent(model, key);
+            if (rank != NO_MATCH) {
+                String group = model.getAlternativeSchemes();
+                matches.add(new Match(name, rank, group == null || 
group.isBlank() ? null : group));
+            }
+        }
+        Map<String, Match> bestPerGroup = new HashMap<>();
+        for (Match match : matches) {
+            if (match.group() != null) {
+                bestPerGroup.merge(match.group(), match, 
CatalogTermMatcher::better);
+            }
+        }
+        matches.removeIf(m -> m.group() != null && bestPerGroup.get(m.group()) 
!= m);
+        return names(matches, max);
+    }
+
+    /**
+     * Data format or language names matching the term, best match first.
+     */
+    static List<String> suggestNames(
+            List<String> names, Function<String, ? extends BaseModel<?>> 
modelLoader, String term, int max) {
+        String key = normalize(term);
+        if (key.isEmpty()) {
+            return List.of();
+        }
+        List<Match> matches = new ArrayList<>();
+        for (String name : names) {
+            BaseModel<?> model = name == null || name.isBlank() ? null : 
modelLoader.apply(name);
+            if (model == null) {
+                continue;
+            }
+            int rank = rankNamed(model, key);
+            if (rank != NO_MATCH) {
+                matches.add(new Match(name, rank, null));
+            }
+        }
+        return names(matches, max);
+    }
+
+    private static int rankComponent(ComponentModel model, String key) {
+        String scheme = model.getScheme();
+        if (key.equals(normalize(scheme))) {
+            return 0;
+        }
+        if (matchesAlias(model, key)) {
+            return 1;
+        }
+        if (model.getAlternativeSchemes() != null && 
containsNormalized(model.getAlternativeSchemes().split(","), key)) {
+            return 2;
+        }
+        if (matchesTitle(model, key)) {
+            return 3;
+        }
+        if (containsNormalized(words(scheme), key)) {
+            return 4;
+        }
+        if (key.length() >= MIN_SUBSTRING_LENGTH && 
normalize(scheme).contains(key)) {
+            return 5;
+        }
+        return NO_MATCH;
+    }
+
+    private static int rankNamed(BaseModel<?> model, String key) {
+        String name = model.getName();
+        if (key.equals(normalize(name))) {
+            return 0;
+        }
+        if (matchesAlias(model, key)) {
+            return 1;
+        }
+        if (matchesTitle(model, key)) {
+            return 2;
+        }
+        if (containsNormalized(camelCaseWords(name), key)) {
+            return 3;
+        }
+        if (key.length() >= MIN_SUBSTRING_LENGTH && 
normalize(name).contains(key)) {
+            return 4;
+        }
+        return NO_MATCH;
+    }
+
+    private static boolean matchesAlias(BaseModel<?> model, String key) {
+        List<String> aliases = model.getAliases();
+        return aliases != null && containsNormalized(aliases.toArray(new 
String[0]), key);
+    }
+
+    private static boolean matchesTitle(BaseModel<?> model, String key) {
+        String title = model.getTitle();
+        return title != null && (key.equals(normalize(title)) || 
containsNormalized(words(title), key));
+    }
+
+    private static Match better(Match a, Match b) {
+        if (a.rank() != b.rank()) {
+            return a.rank() < b.rank() ? a : b;
+        }
+        // same rank: prefer the scheme listed first, which is the primary one
+        List<String> schemes = Arrays.asList(a.group().split(","));
+        return schemes.indexOf(b.name()) < schemes.indexOf(a.name()) ? b : a;
+    }
+
+    private static List<String> names(List<Match> matches, int max) {
+        matches.sort(Comparator.comparingInt(Match::rank));
+        List<String> answer = new ArrayList<>(matches.size());
+        for (Match match : matches) {
+            if (max > 0 && answer.size() >= max) {
+                break;
+            }
+            // a catalog can list a name twice when a component was added on 
top of the built-in ones
+            if (!answer.contains(match.name())) {
+                answer.add(match.name());
+            }
+        }
+        return answer;
+    }
+
+    private static boolean containsNormalized(String[] candidates, String key) 
{
+        for (String candidate : candidates) {
+            if (key.equals(normalize(candidate))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static String[] words(String text) {
+        return text == null ? new String[0] : text.split("[^A-Za-z0-9]+");
+    }
+
+    private static String[] camelCaseWords(String text) {
+        return text == null ? new String[0] : 
words(text.replaceAll("([a-z0-9])([A-Z])", "$1 $2"));
+    }
+
+    private static String normalize(String text) {
+        return text == null ? "" : 
text.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", "");
+    }
+
+}
diff --git 
a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
 
b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
index cc847e65b3c4..b4ea441b8927 100644
--- 
a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
+++ 
b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
@@ -182,6 +182,65 @@ public class CamelCatalogTest {
         assertTrue(names.contains("saga"));
     }
 
+    @Test
+    public void testComponentAliases() {
+        // aliases declared via @Metadata on the endpoint class end up in the 
component model
+        ComponentModel mail = catalog.componentModel("imap");
+        assertNotNull(mail);
+        assertTrue(mail.getAliases().contains("mail"));
+        assertTrue(mail.getAliases().contains("email"));
+        
assertTrue(catalog.componentModel("activemq").getAliases().contains("amq"));
+    }
+
+    @Test
+    public void testSuggestComponentNames() {
+        // exact scheme first, then words of the title or scheme
+        assertEquals(List.of("aws2-s3", "aws2-s3-vectors"), 
catalog.suggestComponentNames("s3", 0));
+        assertEquals(List.of("aws2-sqs"), catalog.suggestComponentNames("sqs", 
0));
+        assertEquals(List.of("paho-mqtt5"), 
catalog.suggestComponentNames("mqtt", 0));
+        assertEquals(List.of("spring-rabbitmq"), 
catalog.suggestComponentNames("rabbitmq", 0));
+        assertEquals(List.of("google-pubsub"), 
catalog.suggestComponentNames("pubsub", 0));
+        assertEquals(List.of("azure-servicebus"), 
catalog.suggestComponentNames("servicebus", 0));
+        assertEquals(List.of("azure-servicebus"), 
catalog.suggestComponentNames("Service-Bus", 0));
+        assertEquals(List.of("kafka", "aws2-msk"), 
catalog.suggestComponentNames("kafka", 0));
+        // aliases rank right after the exact scheme, substring matches last
+        assertEquals(List.of("activemq", "activemq6", "amqp"), 
catalog.suggestComponentNames("amq", 0));
+        assertEquals(List.of("paho-mqtt5"), 
catalog.suggestComponentNames("paho-mqtt", 0));
+        assertTrue(catalog.suggestComponentNames("xyzq", 0).isEmpty());
+        assertTrue(catalog.suggestComponentNames("", 5).isEmpty());
+        assertTrue(catalog.suggestComponentNames(null, 5).isEmpty());
+        // max caps the result
+        List<String> aws = catalog.suggestComponentNames("aws", 0);
+        assertTrue(aws.size() > 5, "aws should match many components, was: " + 
aws);
+        assertEquals(aws.subList(0, 5), catalog.suggestComponentNames("aws", 
5));
+    }
+
+    @Test
+    public void testSuggestComponentNamesDedupesAlternativeSchemes() {
+        // one implementation under several schemes is suggested once, by the 
scheme that matched
+        assertEquals(List.of("smtp"), catalog.suggestComponentNames("smtp", 
0));
+        assertEquals(List.of("https"), catalog.suggestComponentNames("https", 
0));
+        // or by its primary scheme when the match came from an alias
+        List<String> mail = catalog.suggestComponentNames("mail", 0);
+        assertEquals("imap", mail.get(0), "mail should suggest the mail 
component first, was: " + mail);
+        assertFalse(mail.contains("smtp"), "mail should not repeat the mail 
component, was: " + mail);
+        assertTrue(mail.contains("google-mail"), "mail should also suggest 
google-mail, was: " + mail);
+    }
+
+    @Test
+    public void testSuggestDataFormatAndLanguageNames() {
+        assertEquals(List.of("snakeYaml"), 
catalog.suggestDataFormatNames("yaml", 0));
+        List<String> json = catalog.suggestDataFormatNames("json", 0);
+        assertTrue(json.contains("jackson"), "json should suggest jackson, 
was: " + json);
+        assertTrue(json.contains("gson"), "json should suggest gson, was: " + 
json);
+        assertEquals("jackson", catalog.suggestDataFormatNames("jackson", 
0).get(0));
+
+        assertEquals("simple", catalog.suggestLanguageNames("simple", 
0).get(0));
+        List<String> path = catalog.suggestLanguageNames("path", 0);
+        assertTrue(path.contains("xpath"), "path should suggest xpath, was: " 
+ path);
+        assertTrue(path.contains("jsonpath"), "path should suggest jsonpath, 
was: " + path);
+    }
+
     @Test
     public void testEipModelAliases() {
         // verify aliases are present in the EIP model
diff --git 
a/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json
 
b/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json
index a09d44a724dd..d6d6c9c9d608 100644
--- 
a/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json
+++ 
b/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json
@@ -9,6 +9,7 @@
     "label": "messaging",
     "javaType": "org.apache.camel.component.activemq.ActiveMQComponent",
     "supportLevel": "Stable",
+    "aliases": [ "amq" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-activemq",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java
 
b/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java
index be92147a9dc4..c2c8432f5ff0 100644
--- 
a/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java
+++ 
b/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java
@@ -25,12 +25,14 @@ import org.apache.camel.component.jms.JmsComponent;
 import org.apache.camel.component.jms.JmsConfiguration;
 import org.apache.camel.component.jms.JmsEndpoint;
 import org.apache.camel.spi.EndpointServiceLocation;
+import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.UriEndpoint;
 import org.apache.camel.spi.UriParam;
 
 /**
  * Send messages to (or consume from) Apache ActiveMQ 5.x. This component 
extends the Camel JMS component.
  */
+@Metadata(aliases = { "amq" })
 @UriEndpoint(firstVersion = "1.0.0", extendsScheme = "jms", scheme = 
"activemq", title = "ActiveMQ 5.x",
              syntax = "activemq:destinationType:destinationName",
              category = { Category.MESSAGING })
diff --git 
a/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json
 
b/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json
index 04ddf3b3a40b..b1e04a0ba8b4 100644
--- 
a/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json
+++ 
b/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json
@@ -9,6 +9,7 @@
     "label": "messaging",
     "javaType": "org.apache.camel.component.activemq6.ActiveMQComponent",
     "supportLevel": "Stable",
+    "aliases": [ "amq" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-activemq6",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java
 
b/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java
index 877e2fb8a4bf..cd3bed8b2bba 100644
--- 
a/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java
+++ 
b/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java
@@ -25,12 +25,14 @@ import org.apache.camel.component.jms.JmsComponent;
 import org.apache.camel.component.jms.JmsConfiguration;
 import org.apache.camel.component.jms.JmsEndpoint;
 import org.apache.camel.spi.EndpointServiceLocation;
+import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.UriEndpoint;
 import org.apache.camel.spi.UriParam;
 
 /**
  * Send messages to (or consume from) Apache ActiveMQ 6.x. This component 
extends the Camel JMS component.
  */
+@Metadata(aliases = { "amq" })
 @UriEndpoint(firstVersion = "4.7.0", extendsScheme = "jms", scheme = 
"activemq6", title = "ActiveMQ 6.x",
              syntax = "activemq6:destinationType:destinationName",
              category = { Category.MESSAGING })
diff --git 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imap.json
 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imap.json
index 6c49e15393b5..621ba167453a 100644
--- 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imap.json
+++ 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imap.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imaps.json
 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imaps.json
index 2d59fa225fa5..be87ed8c11ae 100644
--- 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imaps.json
+++ 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imaps.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3.json
 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3.json
index 6827a27d73bf..e868fcf6abdd 100644
--- 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3.json
+++ 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3s.json
 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3s.json
index 582c2818dc8a..d2e95187a967 100644
--- 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3s.json
+++ 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3s.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtp.json
 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtp.json
index c0cd0d423a4e..33dff5f867f5 100644
--- 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtp.json
+++ 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtp.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtps.json
 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtps.json
index c5c1721f6c6a..2b2188d35521 100644
--- 
a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtps.json
+++ 
b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtps.json
@@ -9,6 +9,7 @@
     "label": "mail",
     "javaType": "org.apache.camel.component.mail.MailComponent",
     "supportLevel": "Stable",
+    "aliases": [ "mail", "email" ],
     "groupId": "org.apache.camel",
     "artifactId": "camel-mail",
     "version": "4.23.0-SNAPSHOT",
diff --git 
a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java
 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java
index fddb518f54a8..7280476da01a 100644
--- 
a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java
+++ 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java
@@ -30,6 +30,7 @@ import org.apache.camel.spi.EndpointServiceLocation;
 import org.apache.camel.spi.HeaderFilterStrategy;
 import org.apache.camel.spi.HeaderFilterStrategyAware;
 import org.apache.camel.spi.IdempotentRepository;
+import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.UriEndpoint;
 import org.apache.camel.spi.UriParam;
 import org.apache.camel.support.ScheduledPollEndpoint;
@@ -42,6 +43,7 @@ import static 
org.apache.camel.component.mail.MailConstants.MAIL_HANDLE_DUPLICAT
 /**
  * Send and receive emails using imap, pop3 and smtp protocols.
  */
+@Metadata(aliases = { "mail", "email" })
 @UriEndpoint(firstVersion = "1.0.0", scheme = 
"imap,imaps,pop3,pop3s,smtp,smtps", title = "IMAP,IMAPS,POP3,POP3S,SMTP,SMTPS",
              syntax = "imap:host:port", alternativeSyntax = 
"imap:username:password@host:port",
              category = { Category.MAIL }, headersClass = MailConstants.class)
diff --git a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
index ec08bff1087c..7f63d0149d28 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
@@ -88,6 +88,40 @@ nesting level in the YAML DSL, with types, descriptions, 
required flags, default
 and enum choices. It can be used by third-party tooling (editors, IDE plugins, 
visual
 designers) to provide context-aware code completion and validation for YAML 
DSL routes.
 
+=== Finding components by protocol or product name
+
+People, and AI agents driving Camel through tooling, often refer to a 
component by the protocol or
+product it connects to rather than by its Camel scheme: _mqtt_ (the component 
is `paho-mqtt5`),
+_s3_ (`aws2-s3`), _rabbitmq_ (`spring-rabbitmq`), _pubsub_ (`google-pubsub`). 
The catalog can look
+components, data formats and languages up by such a term, ranking the best 
matches first:
+the exact scheme or name, then declared aliases, alternative schemes, words of 
the title, words of the
+scheme or name, and finally names containing the term. A component that shares 
one implementation under
+several schemes (such as `imap`, `pop3` and `smtp`) is returned once.
+
+[source,java]
+----
+CamelCatalog catalog = new DefaultCamelCatalog();
+
+catalog.suggestComponentNames("mqtt", 5);       // [paho-mqtt5]
+catalog.suggestComponentNames("s3", 5);         // [aws2-s3, aws2-s3-vectors]
+catalog.suggestComponentNames("amq", 5);        // [activemq, activemq6, amqp]
+catalog.suggestComponentNames("aws", 5);        // at most five of the many 
AWS components
+catalog.suggestDataFormatNames("yaml", 5);      // [snakeYaml]
+catalog.suggestLanguageNames("path", 5);        // [jsonpath, xpath]
+----
+
+Component developers can declare the shorthands a title cannot provide with 
the `aliases` attribute of
+`@Metadata` on the endpoint class, as the mail component does for _mail_ and 
_email_:
+
+[source,java]
+----
+@Metadata(aliases = { "mail", "email" })
+@UriEndpoint(firstVersion = "1.0.0", scheme = 
"imap,imaps,pop3,pop3s,smtp,smtps", ...)
+public class MailEndpoint extends ScheduledPollEndpoint {
+----
+
+Aliases are descriptive names for discovery only; they cannot be used as the 
scheme in an endpoint URI.
+
 === Documentation in the Catalog
 
 The catalog includes the AsciiDoc documentation pages from all modules. This 
makes the full
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
index b8ac84ee7605..f872d2d5cfac 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
@@ -96,6 +96,13 @@ public abstract class CatalogBaseCommand extends 
CamelCommand {
 
     abstract List<Row> collectRows();
 
+    /**
+     * Names the catalog suggests for a filter that matched nothing, such as a 
protocol or product name (mqtt, s3).
+     */
+    List<String> suggestNames(String term) {
+        return List.of();
+    }
+
     String getGAV(ArtifactModel<?> model) {
         return model.getGroupId() + ":" + model.getArtifactId() + ":" + 
model.getVersion();
     }
@@ -186,7 +193,12 @@ public abstract class CatalogBaseCommand extends 
CamelCommand {
         } else if (filterName != null) {
             // suggest similar names when filter returns no results
             List<String> allNames = collectRows().stream().map(r -> 
r.name).collect(Collectors.toList());
-            List<String> suggestions = 
SuggestSimilarHelper.didYouMean(allNames, filterName);
+            List<String> suggestions = new 
ArrayList<>(suggestNames(filterName));
+            for (String similar : SuggestSimilarHelper.didYouMean(allNames, 
filterName)) {
+                if (!suggestions.contains(similar)) {
+                    suggestions.add(similar);
+                }
+            }
             if (!suggestions.isEmpty()) {
                 printer().println("No results for filter: " + filterName + ". 
Did you mean? " + String.join(", ", suggestions));
             } else {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponent.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponent.java
index 154e96b3f38e..99c766667b6f 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponent.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponent.java
@@ -57,4 +57,9 @@ public class CatalogComponent extends CatalogBaseCommand {
         return rows;
     }
 
+    @Override
+    List<String> suggestNames(String term) {
+        return catalog.suggestComponentNames(term, 5);
+    }
+
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDataFormat.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDataFormat.java
index 14c06a37723b..c785dbb0aa49 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDataFormat.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDataFormat.java
@@ -57,4 +57,9 @@ public class CatalogDataFormat extends CatalogBaseCommand {
         return rows;
     }
 
+    @Override
+    List<String> suggestNames(String term) {
+        return catalog.suggestDataFormatNames(term, 5);
+    }
+
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogLanguage.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogLanguage.java
index 3d641290c184..a1e8a672b17a 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogLanguage.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogLanguage.java
@@ -58,4 +58,9 @@ public class CatalogLanguage extends CatalogBaseCommand {
         return rows;
     }
 
+    @Override
+    List<String> suggestNames(String term) {
+        return catalog.suggestLanguageNames(term, 5);
+    }
+
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponentTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponentTest.java
index e5dd1ed0a6c9..fd0b15bd4a0e 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponentTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponentTest.java
@@ -70,6 +70,17 @@ class CatalogComponentTest extends 
CamelCommandBaseTestSupport {
                 "JSON output should contain a row with name kafka, was: " + 
printer.getOutput());
     }
 
+    @Test
+    void shouldSuggestComponentByProtocolOrProductName() throws Exception {
+        CatalogComponent command = createCommand();
+        command.filterName = "servicebus";
+        int exit = command.doCall();
+        assertEquals(0, exit);
+        String out = printer.getOutput();
+        assertTrue(out.contains("Did you mean? azure-servicebus"),
+                "a product name should suggest the component using it, was: " 
+ out);
+    }
+
     @Test
     void shouldSuggestSimilarWhenFilterHasNoMatch() throws Exception {
         CatalogComponent command = createCommand();
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java
index 147a52da3dce..ac293802f848 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java
@@ -1487,17 +1487,16 @@ class TuiToolRegistry {
             return null;
         }
         String scheme = endpoint.substring(0, colon).toLowerCase();
-        List<String> names;
+        List<String> similar;
         try {
             CamelCatalog catalog = CatalogLoader.loadCatalog(null, 
facade.getSelectedCamelVersion(), true);
-            names = catalog.findComponentNames();
+            if (catalog.findComponentNames().contains(scheme)) {
+                return null;
+            }
+            similar = catalog.suggestComponentNames(scheme, 5);
         } catch (Exception e) {
             return null;
         }
-        if (names.contains(scheme)) {
-            return null;
-        }
-        List<String> similar = suggestComponents(scheme, names);
         if (similar.isEmpty()) {
             return "Camel has no component named '" + scheme + "'. Use 
tui_catalog_doc to find the right component, "
                    + "then send again with its scheme.";
@@ -1507,40 +1506,6 @@ class TuiToolRegistry {
                + endpoint.substring(colon) + "'.";
     }
 
-    /** Well-known names people use for a protocol that differ from the Camel 
component name. */
-    private static final Map<String, List<String>> SCHEME_ALIASES = Map.of(
-            "mqtt", List.of("paho-mqtt5", "paho"),
-            "mqtt5", List.of("paho-mqtt5"),
-            "rabbitmq", List.of("spring-rabbitmq"),
-            "amq", List.of("activemq", "jms"),
-            "rest", List.of("rest", "platform-http", "http"),
-            "s3", List.of("aws2-s3"),
-            "sqs", List.of("aws2-sqs"),
-            "sns", List.of("aws2-sns"),
-            "pubsub", List.of("google-pubsub"),
-            "servicebus", List.of("azure-servicebus"));
-
-    /**
-     * Catalog component names that resemble the scheme: known aliases first, 
then names containing the scheme (or
-     * contained in it), at most five.
-     */
-    static List<String> suggestComponents(String scheme, List<String> 
componentNames) {
-        List<String> result = new ArrayList<>();
-        for (String alias : SCHEME_ALIASES.getOrDefault(scheme, List.of())) {
-            if (componentNames.contains(alias) && !result.contains(alias)) {
-                result.add(alias);
-            }
-        }
-        if (scheme.length() >= 3) {
-            for (String name : componentNames) {
-                if ((name.contains(scheme) || scheme.contains(name)) && 
!name.equals(scheme) && !result.contains(name)) {
-                    result.add(name);
-                }
-            }
-        }
-        return result.size() > 5 ? result.subList(0, 5) : result;
-    }
-
     private String callExecuteSql(Map<String, Object> args) {
         String query = (String) args.get("query");
         if (query == null || query.isBlank()) {
@@ -1818,7 +1783,7 @@ class TuiToolRegistry {
                 return buildComponentDocJson(cm, lowerFilter, includeOptions, 
doc);
             }
             if (kind != null) {
-                return "{\"error\": \"Component not found: " + name + "\"}";
+                return notFound("Component", name, 
catalog.suggestComponentNames(name, 5));
             }
         }
         if (kind == null || "dataformat".equals(kind)) {
@@ -1828,7 +1793,7 @@ class TuiToolRegistry {
                 return buildDataFormatDocJson(dm, lowerFilter, includeOptions, 
doc);
             }
             if (kind != null) {
-                return "{\"error\": \"Data format not found: " + name + "\"}";
+                return notFound("Data format", name, 
catalog.suggestDataFormatNames(name, 5));
             }
         }
         if (kind == null || "language".equals(kind)) {
@@ -1838,7 +1803,7 @@ class TuiToolRegistry {
                 return buildLanguageDocJson(lm, lowerFilter, includeOptions, 
doc);
             }
             if (kind != null) {
-                return "{\"error\": \"Language not found: " + name + "\"}";
+                return notFound("Language", name, 
catalog.suggestLanguageNames(name, 5));
             }
         }
         if (kind == null || "eip".equals(kind)) {
@@ -1851,7 +1816,23 @@ class TuiToolRegistry {
                 return "{\"error\": \"EIP not found: " + name + "\"}";
             }
         }
-        return "{\"error\": \"Artifact not found: " + name + "\"}";
+        List<String> suggestions = new 
ArrayList<>(catalog.suggestComponentNames(name, 5));
+        suggestions.addAll(catalog.suggestDataFormatNames(name, 3));
+        suggestions.addAll(catalog.suggestLanguageNames(name, 3));
+        return notFound("Artifact", name, suggestions);
+    }
+
+    /**
+     * Error for a catalog lookup that found nothing, with the names the 
catalog suggests for the term (a protocol or
+     * product name such as mqtt or s3) so the next call can use one of them.
+     */
+    private static String notFound(String kind, String name, List<String> 
suggestions) {
+        JsonObject error = new JsonObject();
+        error.put("error", kind + " not found: " + name);
+        if (!suggestions.isEmpty()) {
+            error.put("suggestions", new JsonArray(suggestions));
+        }
+        return error.toJson();
     }
 
     @SuppressWarnings("unchecked")
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistrySuggestComponentsTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistrySuggestComponentsTest.java
deleted file mode 100644
index 95e48a617124..000000000000
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistrySuggestComponentsTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.tui;
-
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class TuiToolRegistrySuggestComponentsTest {
-
-    private static final List<String> NAMES = List.of("activemq", "aws2-s3", 
"aws2-sqs", "http", "jms", "kafka",
-            "paho", "paho-mqtt5", "platform-http", "rest", "spring-rabbitmq", 
"timer");
-
-    @Test
-    void knownAliasesComeFirst() {
-        assertEquals(List.of("paho-mqtt5", "paho"), 
TuiToolRegistry.suggestComponents("mqtt", NAMES));
-        assertEquals(List.of("spring-rabbitmq"), 
TuiToolRegistry.suggestComponents("rabbitmq", NAMES));
-        assertEquals(List.of("aws2-s3"), 
TuiToolRegistry.suggestComponents("s3", NAMES));
-    }
-
-    @Test
-    void substringMatchesFillInWhenThereIsNoAlias() {
-        assertEquals(List.of("http"), 
TuiToolRegistry.suggestComponents("https", NAMES));
-        assertEquals(List.of("paho", "paho-mqtt5"), 
TuiToolRegistry.suggestComponents("paho-mqtt", NAMES));
-        assertTrue(TuiToolRegistry.suggestComponents("xyz", NAMES).isEmpty());
-        // very short schemes would match too much, so they only get alias hits
-        assertTrue(TuiToolRegistry.suggestComponents("mq", NAMES).isEmpty());
-    }
-}
diff --git 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
index 6be3dfa1237a..7fde90d74c5f 100644
--- 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
+++ 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
@@ -859,6 +859,12 @@ public class EndpointSchemaGeneratorMojo extends 
AbstractGeneratorMojo {
             model.setFirstVersion(firstVersion);
         }
 
+        // aliases (protocol or product names people use for the component) 
come from @Metadata on the endpoint class
+        Metadata endpointMetadata = 
endpointClassElement.getAnnotation(Metadata.class);
+        if (endpointMetadata != null && endpointMetadata.aliases().length > 0) 
{
+            model.setAliases(new 
ArrayList<>(Arrays.asList(endpointMetadata.aliases())));
+        }
+
         model.setDescription(project.getDescription());
         model.setGroupId(project.getGroupId());
         model.setArtifactId(project.getArtifactId());

Reply via email to