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

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

commit d4d528c488ce3a89756cdb3187aaf65fbd22c342
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Aug 13 15:49:09 2026 +0200

    CAMEL-24390: Address review feedback - use simple pattern matching without 
regex fallback
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../org/apache/camel/catalog/docs/main.adoc        |  2 +-
 .../org/apache/camel/catalog/docs/mcp-server.adoc  | 17 +++--
 .../component/mcp/server/McpServerBridge.java      |  2 +-
 .../mcp/server/McpServerConfiguration.java         |  6 +-
 .../camel-main-configuration-metadata.json         |  2 +-
 core/camel-main/src/main/docs/main.adoc            |  2 +-
 .../main/HttpServerConfigurationProperties.java    | 12 ++--
 .../org/apache/camel/support/PatternHelper.java    | 48 ++++++++++++++
 .../apache/camel/support/PatternHelperTest.java    | 73 ++++++++++++++++++++++
 9 files changed, 145 insertions(+), 19 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
index 3fe96305eab4..8ed7976b3865 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
@@ -220,7 +220,7 @@ The camel.server supports 28 options, which are listed 
below.
 | *camel.server.mcpServerName* | MCP server name advertised to clients. 
Defaults to the CamelContext name. |  | String
 | *camel.server.mcpSessionIdleTtl* | Idle TTL in milliseconds for MCP sessions 
on the Vert.x streamable transport. Sessions with no activity for longer than 
this interval are evicted. 0 disables idle eviction. | 300000 | long
 | *camel.server.mcpSessionKeepAliveInterval* | Keep-alive ping interval in 
milliseconds for MCP sessions on the Vert.x streamable transport. Dead sessions 
are evicted after consecutive ping failures. 0 disables keep-alive pings. | 
30000 | long
-| *camel.server.mcpTags* | Comma-separated list of ai-tool tags to expose as 
MCP tools. Only tools registered under one of these tags are exposed; the 
untagged default pool is never exposed. When not set, no tools are exposed. |  
| String
+| *camel.server.mcpTags* | Comma-separated list of ai-tool tag patterns to 
expose as MCP tools. Matching is case-insensitive and supports exact match, 
wildcard prefix ( {code foo} ), and {code } to match all tags. Only tools 
registered under a matching tag are exposed; the untagged default pool is never 
exposed. When not set, no tools are exposed. |  | String
 | *camel.server.mcpToolTimeout* | Per-call MCP tool execution timeout in 
milliseconds. A call exceeding the timeout returns an error result to the MCP 
client; the underlying route keeps running until it completes on its own. | 
20000 | long
 | *camel.server.path* | Context-path to use for embedded HTTP server | / | 
String
 | *camel.server.port* | Port to use for binding embedded HTTP server. Use 0 to 
dynamic assign a free random port number. | 8080 | int
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mcp-server.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mcp-server.adoc
index fdc8fdef6e3a..6b761dc26029 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mcp-server.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mcp-server.adoc
@@ -120,6 +120,9 @@ camel.server.mcp-tags = crm,notify
 camel.server.mcp-server-name = my-integration-app
 ----
 
+Tag patterns support wildcards: use `*` to expose all tagged tools, or a
+prefix pattern like `crm*` to expose all tags starting with `crm`:
+
 On other runtimes, or when wiring programmatically, add the
 `McpServerBridge` service to the CamelContext instead:
 
@@ -157,10 +160,11 @@ JBang (see the xref:main.adoc[camel-main] options) or on
 
 | `camel.server.mcp-enabled` | Whether to expose ai-tool routes as MCP tools
   over streamable HTTP. | `false` | bridge
-| `camel.server.mcp-tags` | Comma-separated list of ai-tool tags to expose as
-  MCP tools. Only tools registered under one of these tags are published; the
-  untagged default pool is never exposed. When not set, no tools are
-  published. |  | bridge
+| `camel.server.mcp-tags` | Comma-separated list of ai-tool tag patterns to
+  expose as MCP tools. Patterns support exact match, wildcard prefix
+  (`foo*`), and `*` to match all tags. Only tools registered under a matching
+  tag are published; the untagged default pool is never exposed. When not
+  set, no tools are published. |  | bridge
 | `camel.server.mcp-tool-timeout` | Per-call tool execution timeout in
   milliseconds. A call exceeding the timeout returns an error result to the
   MCP client; the underlying route keeps running until it completes on its
@@ -215,8 +219,9 @@ External MCP clients are *untrusted senders* under the
 xref:manual::security-model.adoc[Camel security model]. The module applies the
 following rules:
 
-* *Explicit opt-in per tool*: only tools whose tags intersect the configured
-  `tags` are exposed. The untagged default pool is never exposed implicitly.
+* *Explicit opt-in per tool*: only tools whose tags match the configured tag
+  patterns are exposed. The untagged default pool is never exposed, even when
+  using the `*` wildcard.
 * *Flat namespace protection*: a tool whose name collides with an already
   exposed tool is refused with an ERROR log — never silently replaced.
 * *Error sanitization*: route exceptions are mapped to a generic error
diff --git 
a/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerBridge.java
 
b/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerBridge.java
index 91abc3f11f1c..277833c5b3ec 100644
--- 
a/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerBridge.java
+++ 
b/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerBridge.java
@@ -304,7 +304,7 @@ public class McpServerBridge extends ServiceSupport 
implements CamelContextAware
     }
 
     private boolean matchesTag(String tag) {
-        return tag != null && PatternHelper.matchPatterns(tag, tagPatterns);
+        return tag != null && PatternHelper.matchSimplePatterns(tag, 
tagPatterns);
     }
 
     private final class RegistryListener implements AiToolRegistryListener {
diff --git 
a/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerConfiguration.java
 
b/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerConfiguration.java
index e2f69c04d099..09fed0658340 100644
--- 
a/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerConfiguration.java
+++ 
b/components/camel-ai/camel-mcp-server-api/src/main/java/org/apache/camel/component/mcp/server/McpServerConfiguration.java
@@ -35,9 +35,9 @@ public class McpServerConfiguration {
     private long sessionIdleTtl = McpServerConstants.DEFAULT_SESSION_IDLE_TTL;
 
     /**
-     * Comma-separated list of ai-tool tag patterns to expose as MCP tools. 
Patterns support exact match, wildcard
-     * prefix ({@code foo*}), and {@code *} to match all tags. Only tools 
registered under a matching tag are published;
-     * the untagged default pool is never exposed. When not set, no tools are 
published.
+     * Comma-separated list of ai-tool tag patterns to expose as MCP tools. 
Matching is case-insensitive and supports
+     * exact match, wildcard prefix ({@code foo*}), and {@code *} to match all 
tags. Only tools registered under a
+     * matching tag are published; the untagged default pool is never exposed. 
When not set, no tools are published.
      */
     public String getTags() {
         return tags;
diff --git 
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
 
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
index 1146c05cd1cc..6287ad28e0ae 100644
--- 
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
+++ 
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
@@ -445,7 +445,7 @@
     { "name": "camel.server.mcpServerName", "required": false, "description": 
"MCP server name advertised to clients. Defaults to the CamelContext name.", 
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties", 
"type": "string", "javaType": "java.lang.String", "secret": false },
     { "name": "camel.server.mcpSessionIdleTtl", "required": false, 
"description": "Idle TTL in milliseconds for MCP sessions on the Vert.x 
streamable transport. Sessions with no activity for longer than this interval 
are evicted. 0 disables idle eviction.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "long", "defaultValue": 300000, "secret": false },
     { "name": "camel.server.mcpSessionKeepAliveInterval", "required": false, 
"description": "Keep-alive ping interval in milliseconds for MCP sessions on 
the Vert.x streamable transport. Dead sessions are evicted after consecutive 
ping failures. 0 disables keep-alive pings.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "long", "defaultValue": 30000, "secret": false },
-    { "name": "camel.server.mcpTags", "required": false, "description": 
"Comma-separated list of ai-tool tags to expose as MCP tools. Only tools 
registered under one of these tags are exposed; the untagged default pool is 
never exposed. When not set, no tools are exposed.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "secret": false },
+    { "name": "camel.server.mcpTags", "required": false, "description": 
"Comma-separated list of ai-tool tag patterns to expose as MCP tools. Matching 
is case-insensitive and supports exact match, wildcard prefix ( {code foo} ), 
and {code } to match all tags. Only tools registered under a matching tag are 
exposed; the untagged default pool is never exposed. When not set, no tools are 
exposed.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"ja [...]
     { "name": "camel.server.mcpToolTimeout", "required": false, "description": 
"Per-call MCP tool execution timeout in milliseconds. A call exceeding the 
timeout returns an error result to the MCP client; the underlying route keeps 
running until it completes on its own.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "long", "defaultValue": 20000, "secret": false },
     { "name": "camel.server.path", "required": false, "description": 
"Context-path to use for embedded HTTP server", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "defaultValue": "\/", "secret": false },
     { "name": "camel.server.port", "required": false, "description": "Port to 
use for binding embedded HTTP server. Use 0 to dynamic assign a free random 
port number.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "int", "defaultValue": 8080, "secret": false },
diff --git a/core/camel-main/src/main/docs/main.adoc 
b/core/camel-main/src/main/docs/main.adoc
index 3fe96305eab4..8ed7976b3865 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -220,7 +220,7 @@ The camel.server supports 28 options, which are listed 
below.
 | *camel.server.mcpServerName* | MCP server name advertised to clients. 
Defaults to the CamelContext name. |  | String
 | *camel.server.mcpSessionIdleTtl* | Idle TTL in milliseconds for MCP sessions 
on the Vert.x streamable transport. Sessions with no activity for longer than 
this interval are evicted. 0 disables idle eviction. | 300000 | long
 | *camel.server.mcpSessionKeepAliveInterval* | Keep-alive ping interval in 
milliseconds for MCP sessions on the Vert.x streamable transport. Dead sessions 
are evicted after consecutive ping failures. 0 disables keep-alive pings. | 
30000 | long
-| *camel.server.mcpTags* | Comma-separated list of ai-tool tags to expose as 
MCP tools. Only tools registered under one of these tags are exposed; the 
untagged default pool is never exposed. When not set, no tools are exposed. |  
| String
+| *camel.server.mcpTags* | Comma-separated list of ai-tool tag patterns to 
expose as MCP tools. Matching is case-insensitive and supports exact match, 
wildcard prefix ( {code foo} ), and {code } to match all tags. Only tools 
registered under a matching tag are exposed; the untagged default pool is never 
exposed. When not set, no tools are exposed. |  | String
 | *camel.server.mcpToolTimeout* | Per-call MCP tool execution timeout in 
milliseconds. A call exceeding the timeout returns an error result to the MCP 
client; the underlying route keeps running until it completes on its own. | 
20000 | long
 | *camel.server.path* | Context-path to use for embedded HTTP server | / | 
String
 | *camel.server.port* | Port to use for binding embedded HTTP server. Use 0 to 
dynamic assign a free random port number. | 8080 | int
diff --git 
a/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
 
b/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
index b319500f6e36..1bf2b23fe5b5 100644
--- 
a/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
+++ 
b/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
@@ -358,9 +358,9 @@ public class HttpServerConfigurationProperties implements 
BootstrapCloseable {
     }
 
     /**
-     * Comma-separated list of ai-tool tag patterns to expose as MCP tools. 
Patterns support exact match, wildcard prefix
-     * ({@code foo*}), and {@code *} to match all tags. Only tools registered 
under a matching tag are exposed; the
-     * untagged default pool is never exposed. When not set, no tools are 
exposed.
+     * Comma-separated list of ai-tool tag patterns to expose as MCP tools. 
Matching is case-insensitive and supports
+     * exact match, wildcard prefix ({@code foo*}), and {@code *} to match all 
tags. Only tools registered under a
+     * matching tag are exposed; the untagged default pool is never exposed. 
When not set, no tools are exposed.
      */
     public void setMcpTags(String mcpTags) {
         this.mcpTags = mcpTags;
@@ -614,9 +614,9 @@ public class HttpServerConfigurationProperties implements 
BootstrapCloseable {
     }
 
     /**
-     * Comma-separated list of ai-tool tag patterns to expose as MCP tools. 
Patterns support exact match, wildcard prefix
-     * ({@code foo*}), and {@code *} to match all tags. Only tools registered 
under a matching tag are exposed; the
-     * untagged default pool is never exposed. When not set, no tools are 
exposed.
+     * Comma-separated list of ai-tool tag patterns to expose as MCP tools. 
Matching is case-insensitive and supports
+     * exact match, wildcard prefix ({@code foo*}), and {@code *} to match all 
tags. Only tools registered under a
+     * matching tag are exposed; the untagged default pool is never exposed. 
When not set, no tools are exposed.
      */
     public HttpServerConfigurationProperties withMcpTags(String mcpTags) {
         this.mcpTags = mcpTags;
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/PatternHelper.java 
b/core/camel-support/src/main/java/org/apache/camel/support/PatternHelper.java
index dd784ae5ae47..04c1e0feb279 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/PatternHelper.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/PatternHelper.java
@@ -92,6 +92,54 @@ public final class PatternHelper {
         return false;
     }
 
+    /**
+     * Matches the name with the given pattern (case insensitive) using simple 
matching only (no regular expressions).
+     * <p/>
+     * The match rules are applied in this order:
+     * <ul>
+     * <li>exact match (case insensitive), returns true</li>
+     * <li>wildcard match (pattern ends with a * and the name starts with the 
pattern), returns true</li>
+     * <li>otherwise returns false</li>
+     * </ul>
+     *
+     * @param  name    the name
+     * @param  pattern a pattern to match
+     * @return         <tt>true</tt> if match, <tt>false</tt> otherwise.
+     * @see            #matchSimplePatterns(String, String[])
+     */
+    public static boolean matchSimplePattern(String name, String pattern) {
+        if (name == null || pattern == null) {
+            return false;
+        }
+
+        if (name.equalsIgnoreCase(pattern)) {
+            return true;
+        }
+
+        if ("*".equals(pattern)) {
+            return true;
+        }
+
+        return matchWildcard(name, pattern);
+    }
+
+    /**
+     * Matches the name with the given patterns (case insensitive) using 
simple matching only (no regular expressions).
+     *
+     * @param  name     the name
+     * @param  patterns pattern(s) to match
+     * @return          <tt>true</tt> if match, <tt>false</tt> otherwise.
+     * @see             #matchSimplePattern(String, String)
+     */
+    public static boolean matchSimplePatterns(String name, String[] patterns) {
+        for (String pattern : patterns) {
+            if (PatternHelper.matchSimplePattern(name, pattern)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Matches the name with the given pattern (case insensitive).
      * <p/>
diff --git 
a/core/camel-support/src/test/java/org/apache/camel/support/PatternHelperTest.java
 
b/core/camel-support/src/test/java/org/apache/camel/support/PatternHelperTest.java
new file mode 100644
index 000000000000..2fceae7208b5
--- /dev/null
+++ 
b/core/camel-support/src/test/java/org/apache/camel/support/PatternHelperTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.support;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class PatternHelperTest {
+
+    @Test
+    void testSimplePatternExactMatch() {
+        assertTrue(PatternHelper.matchSimplePattern("crm", "crm"));
+        assertTrue(PatternHelper.matchSimplePattern("crm", "CRM"));
+        assertTrue(PatternHelper.matchSimplePattern("CRM", "crm"));
+    }
+
+    @Test
+    void testSimplePatternNoMatch() {
+        assertFalse(PatternHelper.matchSimplePattern("crm", "erp"));
+    }
+
+    @Test
+    void testSimplePatternWildcardAll() {
+        assertTrue(PatternHelper.matchSimplePattern("crm", "*"));
+        assertTrue(PatternHelper.matchSimplePattern("anything", "*"));
+    }
+
+    @Test
+    void testSimplePatternWildcardPrefix() {
+        assertTrue(PatternHelper.matchSimplePattern("crm-tools", "crm*"));
+        assertTrue(PatternHelper.matchSimplePattern("CRM-Tools", "crm*"));
+        assertFalse(PatternHelper.matchSimplePattern("erp-tools", "crm*"));
+    }
+
+    @Test
+    void testSimplePatternNoRegexFallback() {
+        // "a.b" as regex would match "axb" — simple pattern must NOT do this
+        assertFalse(PatternHelper.matchSimplePattern("axb", "a.b"));
+        // contrast with the full matchPattern which does fall back to regex
+        assertTrue(PatternHelper.matchPattern("axb", "a.b"));
+    }
+
+    @Test
+    void testSimplePatternNullSafety() {
+        assertFalse(PatternHelper.matchSimplePattern(null, "crm"));
+        assertFalse(PatternHelper.matchSimplePattern("crm", null));
+        assertFalse(PatternHelper.matchSimplePattern(null, null));
+    }
+
+    @Test
+    void testSimplePatternsMultiple() {
+        String[] patterns = { "crm", "erp*" };
+        assertTrue(PatternHelper.matchSimplePatterns("crm", patterns));
+        assertTrue(PatternHelper.matchSimplePatterns("erp-tools", patterns));
+        assertFalse(PatternHelper.matchSimplePatterns("billing", patterns));
+    }
+}

Reply via email to