Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
"maxToolIterations": { "index": 28, "kind": "parameter", "displayName": "Max Tool Iterations", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 50, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Maximum number of tool call loop iterations to prevent infinite loops" },
"mcpProtocolVersions": { "index": 29, "kind": "parameter", "displayName": "Mcp Protocol Versions", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Comma-separated list of MCP protocol versions to advertise when connecting to MCP servers using Streamable HTTP transport. When not set, the SDK default is used. Example: 2024-11-05,2025-03-26,2025-06-18" },
"mcpReconnect": { "index": 30, "kind": "parameter", "displayName": "Mcp Reconnect", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Automatically reconnect to MCP servers when a tool call fails due to a transport error, and retry the call once." },
"mcpServer": { "index": 31, "kind": "parameter", "displayName": "Mcp Server", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": "mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "MCP (Model Context Protocol) server configurations. Define servers using prefix notation: mcpServer..transportType=stdiossestreamableHttp, (Note that sse is deprecated) mcpServer..command= (stdio), mcpServer..args= (stdio), mcpServer..url= (sse\/streamableHttp), mcpServer..oauthProfile= (OAuth profile for HTTP auth, requires camel-oauth). This is a multi-value option with prefix: mcpServer." },
"mcpServer": { "index": 31, "kind": "parameter", "displayName": "Mcp Server", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": "mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "MCP (Model Context Protocol) server configurations. Define servers using prefix notation: mcpServer..transportType=stdiossestreamableHttp, (Note that sse is deprecated) mcpServer..command= (stdio), mcpServer..args= (stdio), mcpServer..url= (sse\/streamableHttp), mcpServer..oauthProfile= (OAuth profile for HTTP auth, requires camel-oauth), mcpServer..toolNames= (optional include list to restrict which tools are registered from this server). This is a multi-value option with prefix: mcpServer." },
"mcpTimeout": { "index": 32, "kind": "parameter", "displayName": "Mcp Timeout", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 20, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Timeout in seconds for MCP tool call requests. Applies to all MCP operations including tool execution and initialization." },
"model": { "index": 33, "kind": "parameter", "displayName": "Model", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The model to use for chat completion" },
"outputClass": { "index": 34, "kind": "parameter", "displayName": "Output Class", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Fully qualified class name for structured output using response format" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,64 @@ YAML::
----
====

==== Tool Filtering

By default, all tools from every configured MCP server are registered and advertised to the model.
Use the per-server `toolNames` property to restrict which tools are included. This is useful to
reduce prompt size and cost, keep destructive tools away from the model, or avoid duplicate tool
names across servers.

[tabs]
====
Java::
+
[source,java]
----
from("direct:chat")
.to("openai:chat-completion?model=gpt-4"
+ "&mcpServer.fs.transportType=stdio"
+ "&mcpServer.fs.command=npx"
+ "&mcpServer.fs.args=-y,@modelcontextprotocol/server-filesystem,/tmp"
+ "&mcpServer.fs.toolNames=read_file,list_directory");
----

XML::
+
[source,xml]
----
<route>
<from uri="direct:chat"/>
<to uri="openai:chat-completion?model=gpt-4&amp;mcpServer.fs.transportType=stdio&amp;mcpServer.fs.command=npx&amp;mcpServer.fs.args=-y,@modelcontextprotocol/server-filesystem,/tmp&amp;mcpServer.fs.toolNames=read_file,list_directory"/>
</route>
----

YAML::
+
[source,yaml]
----
- route:
from:
uri: direct:chat
steps:
- to:
uri: openai:chat-completion
parameters:
model: gpt-4
mcpServer.fs.transportType: stdio
mcpServer.fs.command: npx
mcpServer.fs.args: "-y,@modelcontextprotocol/server-filesystem,/tmp"
mcpServer.fs.toolNames: read_file,list_directory
----
====

When `toolNames` is set, only tools whose names appear in the comma-separated list are registered.
Tools not in the list are silently excluded. If a name in the list does not match any tool provided
by the server, a warning is logged. When `toolNames` is not set or is empty, all tools from the
server are registered (the default behavior).

The filter is also applied consistently on MCP server reconnection (`mcpReconnect=true`), so the
tool set remains stable across connection recovery.

=== Agentic Loop Behavior

When the model responds with tool calls, the component automatically:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
"maxToolIterations": { "index": 28, "kind": "parameter", "displayName": "Max Tool Iterations", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 50, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Maximum number of tool call loop iterations to prevent infinite loops" },
"mcpProtocolVersions": { "index": 29, "kind": "parameter", "displayName": "Mcp Protocol Versions", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Comma-separated list of MCP protocol versions to advertise when connecting to MCP servers using Streamable HTTP transport. When not set, the SDK default is used. Example: 2024-11-05,2025-03-26,2025-06-18" },
"mcpReconnect": { "index": 30, "kind": "parameter", "displayName": "Mcp Reconnect", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Automatically reconnect to MCP servers when a tool call fails due to a transport error, and retry the call once." },
"mcpServer": { "index": 31, "kind": "parameter", "displayName": "Mcp Server", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": "mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "MCP (Model Context Protocol) server configurations. Define servers using prefix notation: mcpServer..transportType=stdiossestreamableHttp, (Note that sse is deprecated) mcpServer..command= (stdio), mcpServer..args= (stdio), mcpServer..url= (sse\/streamableHttp), mcpServer..oauthProfile= (OAuth profile for HTTP auth, requires camel-oauth). This is a multi-value option with prefix: mcpServer." },
"mcpServer": { "index": 31, "kind": "parameter", "displayName": "Mcp Server", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": "mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "MCP (Model Context Protocol) server configurations. Define servers using prefix notation: mcpServer..transportType=stdiossestreamableHttp, (Note that sse is deprecated) mcpServer..command= (stdio), mcpServer..args= (stdio), mcpServer..url= (sse\/streamableHttp), mcpServer..oauthProfile= (OAuth profile for HTTP auth, requires camel-oauth), mcpServer..toolNames= (optional include list to restrict which tools are registered from this server). This is a multi-value option with prefix: mcpServer." },
"mcpTimeout": { "index": 32, "kind": "parameter", "displayName": "Mcp Timeout", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 20, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Timeout in seconds for MCP tool call requests. Applies to all MCP operations including tool execution and initialization." },
"model": { "index": 33, "kind": "parameter", "displayName": "Model", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "The model to use for chat completion" },
"outputClass": { "index": 34, "kind": "parameter", "displayName": "Output Class", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": "configuration", "description": "Fully qualified class name for structured output using response format" },
Expand Down
58 changes: 58 additions & 0 deletions components/camel-ai/camel-openai/src/main/docs/openai-mcp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,64 @@ YAML::
----
====

==== Tool Filtering

By default, all tools from every configured MCP server are registered and advertised to the model.
Use the per-server `toolNames` property to restrict which tools are included. This is useful to
reduce prompt size and cost, keep destructive tools away from the model, or avoid duplicate tool
names across servers.

[tabs]
====
Java::
+
[source,java]
----
from("direct:chat")
.to("openai:chat-completion?model=gpt-4"
+ "&mcpServer.fs.transportType=stdio"
+ "&mcpServer.fs.command=npx"
+ "&mcpServer.fs.args=-y,@modelcontextprotocol/server-filesystem,/tmp"
+ "&mcpServer.fs.toolNames=read_file,list_directory");
----

XML::
+
[source,xml]
----
<route>
<from uri="direct:chat"/>
<to uri="openai:chat-completion?model=gpt-4&amp;mcpServer.fs.transportType=stdio&amp;mcpServer.fs.command=npx&amp;mcpServer.fs.args=-y,@modelcontextprotocol/server-filesystem,/tmp&amp;mcpServer.fs.toolNames=read_file,list_directory"/>
</route>
----

YAML::
+
[source,yaml]
----
- route:
from:
uri: direct:chat
steps:
- to:
uri: openai:chat-completion
parameters:
model: gpt-4
mcpServer.fs.transportType: stdio
mcpServer.fs.command: npx
mcpServer.fs.args: "-y,@modelcontextprotocol/server-filesystem,/tmp"
mcpServer.fs.toolNames: read_file,list_directory
----
====

When `toolNames` is set, only tools whose names appear in the comma-separated list are registered.
Tools not in the list are silently excluded. If a name in the list does not match any tool provided
by the server, a warning is logged. When `toolNames` is not set or is empty, all tools from the
server are registered (the default behavior).

The filter is also applied consistently on MCP server reconnection (`mcpReconnect=true`), so the
tool set remains stable across connection recovery.

=== Agentic Loop Behavior

When the model responds with tool calls, the component automatically:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ public class OpenAIConfiguration implements Cloneable {
+ "Define servers using prefix notation: mcpServer.<name>.transportType=stdio|sse|streamableHttp, (Note that sse is deprecated) "
+ "mcpServer.<name>.command=<cmd> (stdio), mcpServer.<name>.args=<comma-separated> (stdio), "
+ "mcpServer.<name>.url=<url> (sse/streamableHttp), "
+ "mcpServer.<name>.oauthProfile=<profile> (OAuth profile for HTTP auth, requires camel-oauth)")
+ "mcpServer.<name>.oauthProfile=<profile> (OAuth profile for HTTP auth, requires camel-oauth), "
+ "mcpServer.<name>.toolNames=<comma-separated> (optional include list to restrict which tools are registered from this server)")
private Map<String, Object> mcpServer;

@UriParam(defaultValue = "50")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ private void initializeMcpServers() throws Exception {
LOG.debug("MCP server '{}' initialized, listing tools", serverName);

McpSchema.ListToolsResult toolsResult = mcpClient.listTools();
List<McpSchema.Tool> serverTools = toolsResult.tools();
List<McpSchema.Tool> serverTools = filterTools(toolsResult.tools(), serverName, props);

for (McpSchema.Tool tool : serverTools) {
if (toolClientMap.putIfAbsent(tool.name(), mcpClient) != null) {
Expand Down Expand Up @@ -363,7 +363,7 @@ private McpSyncClient doReconnectMcpServer(McpSyncClient oldClient, String serve
try {
McpSyncClient newClient = createMcpClient(serverName, props);

List<McpSchema.Tool> tools = newClient.listTools().tools();
List<McpSchema.Tool> tools = filterTools(newClient.listTools().tools(), serverName, props);

globalMcpLock.lock();
try {
Expand Down Expand Up @@ -412,6 +412,45 @@ private McpSyncClient doReconnectMcpServer(McpSyncClient oldClient, String serve
}
}

/**
* Filters the tools listed from an MCP server according to the per-server {@code toolNames} include list. When no
* {@code toolNames} property is configured, all tools are returned unchanged.
*
* @param allTools the full list of tools from the server
* @param serverName the logical server name (for logging)
* @param props the per-server configuration properties
* @return the filtered tool list (or the original list if no filter is configured)
*/
private List<McpSchema.Tool> filterTools(List<McpSchema.Tool> allTools, String serverName, Map<String, String> props) {
String toolNamesCsv = props.get("toolNames");
if (toolNamesCsv == null || toolNamesCsv.isBlank()) {
return allTools;
}

Set<String> allowed = new HashSet<>();
for (String name : toolNamesCsv.split(",")) {
String trimmed = name.trim();
if (!trimmed.isEmpty()) {
allowed.add(trimmed);
}
}

List<McpSchema.Tool> filtered = allTools.stream()
.filter(t -> allowed.contains(t.name()))
.toList();

Set<String> found = filtered.stream().map(McpSchema.Tool::name).collect(Collectors.toSet());
Set<String> missing = new HashSet<>(allowed);
missing.removeAll(found);
if (!missing.isEmpty()) {
LOG.warn("MCP server '{}' does not provide the following toolNames: {}", serverName, missing);
}

LOG.info("MCP server '{}': filtered {} tools to {} via toolNames include list",
serverName, allTools.size(), filtered.size());
return filtered;
}

private Set<String> toolsForServer(String serverName) {
return mcpToolState.toolToServerName().entrySet().stream()
.filter(e -> serverName.equals(e.getValue()))
Expand Down
Loading