Repository navigation
Add support filter tool/resources #301
Copy link
Copy link
Closed as duplicate
Labels
duplicateThis issue or pull request already existsThis issue or pull request already existsenhancementNew feature or requestNew feature or requestfeature/repositories
Description
Activity
yes, we have this need as well.
i took a crack at adding support for filtering tool lists to the project (note that this diff also includes the context patch from #226 ):
diff --git a/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java b/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java index ef1e512..eb9a824 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java +++ b/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java @@ -14,9 +14,11 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.BiFunction; +import java.util.function.Predicate; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import io.modelcontextprotocol.server.McpServerFeatures.AsyncToolSpecification; import io.modelcontextprotocol.spec.*; import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.LoggingLevel; @@ -25,6 +27,7 @@ import io.modelcontextprotocol.spec.McpSchema.ResourceTemplate; import io.modelcontextprotocol.spec.McpSchema.SetLevelRequest; import io.modelcontextprotocol.spec.McpSchema.Tool; import io.modelcontextprotocol.util.DeafaultMcpUriTemplateManagerFactory; +import io.modelcontextprotocol.util.McpAsyncToolFilterFactory; import io.modelcontextprotocol.util.McpUriTemplateManagerFactory; import io.modelcontextprotocol.util.Utils; import org.slf4j.Logger; @@ -106,6 +109,8 @@ public class McpAsyncServer { private McpUriTemplateManagerFactory uriTemplateManagerFactory = new DeafaultMcpUriTemplateManagerFactory(); + private final McpAsyncToolFilterFactory toolFilterFactory; + /** * Create a new McpAsyncServer with the given transport provider and capabilities. * @param mcpTransportProvider The transport layer implementation for MCP @@ -115,7 +120,7 @@ public class McpAsyncServer { */ McpAsyncServer(McpServerTransportProvider mcpTransportProvider, ObjectMapper objectMapper, McpServerFeatures.Async features, Duration requestTimeout, - McpUriTemplateManagerFactory uriTemplateManagerFactory) { + McpUriTemplateManagerFactory uriTemplateManagerFactory, McpAsyncToolFilterFactory toolFilterFactory) { this.mcpTransportProvider = mcpTransportProvider; this.objectMapper = objectMapper; this.serverInfo = features.serverInfo(); @@ -127,6 +132,7 @@ public class McpAsyncServer { this.prompts.putAll(features.prompts()); this.completions.putAll(features.completions()); this.uriTemplateManagerFactory = uriTemplateManagerFactory; + this.toolFilterFactory = toolFilterFactory; Map<String, McpServerSession.RequestHandler<?>> requestHandlers = new HashMap<>(); @@ -341,7 +347,14 @@ public class McpAsyncServer { private McpServerSession.RequestHandler<McpSchema.ListToolsResult> toolsListRequestHandler() { return (exchange, params, context) -> { - List<Tool> tools = this.tools.stream().map(McpServerFeatures.AsyncToolSpecification::tool).toList(); + + Predicate<AsyncToolSpecification> toolsFilter = toolFilterFactory != null + ? toolFilterFactory.createFilter(exchange, params, context) : t -> true; + + List<Tool> tools = this.tools.stream() + .filter(toolsFilter) + .map(McpServerFeatures.AsyncToolSpecification::tool) + .toList(); return Mono.just(new McpSchema.ListToolsResult(tools, null)); }; diff --git a/mcp/src/main/java/io/modelcontextprotocol/server/McpServer.java b/mcp/src/main/java/io/modelcontextprotocol/server/McpServer.java index 083beb6..3c7c00b 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/server/McpServer.java +++ b/mcp/src/main/java/io/modelcontextprotocol/server/McpServer.java @@ -20,6 +20,8 @@ import io.modelcontextprotocol.spec.McpSchema.ResourceTemplate; import io.modelcontextprotocol.spec.McpServerTransportProvider; import io.modelcontextprotocol.util.Assert; import io.modelcontextprotocol.util.DeafaultMcpUriTemplateManagerFactory; +import io.modelcontextprotocol.util.McpAsyncToolFilterFactory; +import io.modelcontextprotocol.util.McpSyncToolFilterFactory; import io.modelcontextprotocol.util.McpUriTemplateManagerFactory; import reactor.core.publisher.Mono; @@ -203,6 +205,8 @@ public interface McpServer { private Duration requestTimeout = Duration.ofSeconds(10); // Default timeout + private McpAsyncToolFilterFactory toolFilterFactory; + private AsyncSpecification(McpServerTransportProvider transportProvider) { Assert.notNull(transportProvider, "Transport provider must not be null"); this.transportProvider = transportProvider; @@ -624,6 +628,18 @@ public interface McpServer { return this; } + /** + * Sets the tool filter factory that will be used to create filters for tool + * lists. + * @param toolFilterFactory The factory to use. Must not be null. + * @return This builder instance for method chaining. + */ + public AsyncSpecification toolFilterFactory(McpAsyncToolFilterFactory toolFilterFactory) { + Assert.notNull(toolFilterFactory, "Tool filter factory must not be null"); + this.toolFilterFactory = toolFilterFactory; + return this; + } + /** * Builds an asynchronous MCP server that provides non-blocking operations. * @return A new instance of {@link McpAsyncServer} configured with this builder's @@ -635,7 +651,7 @@ public interface McpServer { this.instructions); var mapper = this.objectMapper != null ? this.objectMapper : new ObjectMapper(); return new McpAsyncServer(this.transportProvider, mapper, features, this.requestTimeout, - this.uriTemplateManagerFactory); + this.uriTemplateManagerFactory, this.toolFilterFactory); } } @@ -695,6 +711,8 @@ public interface McpServer { private Duration requestTimeout = Duration.ofSeconds(10); // Default timeout + private McpSyncToolFilterFactory toolFilterFactory; + private SyncSpecification(McpServerTransportProvider transportProvider) { Assert.notNull(transportProvider, "Transport provider must not be null"); this.transportProvider = transportProvider; @@ -1116,6 +1134,18 @@ public interface McpServer { return this; } + /** + * Sets the tool filter factory that will be used to create filters for tool + * lists. + * @param toolFilterFactory The factory to use. Must not be null. + * @return This builder instance for method chaining. + */ + public SyncSpecification toolFilterFactory(McpSyncToolFilterFactory toolFilterFactory) { + Assert.notNull(toolFilterFactory, "Tool filter factory must not be null"); + this.toolFilterFactory = toolFilterFactory; + return this; + } + /** * Builds a synchronous MCP server that provides blocking operations. * @return A new instance of {@link McpSyncServer} configured with this builder's @@ -1127,8 +1157,10 @@ public interface McpServer { this.rootsChangeHandlers, this.instructions); McpServerFeatures.Async asyncFeatures = McpServerFeatures.Async.fromSync(syncFeatures); var mapper = this.objectMapper != null ? this.objectMapper : new ObjectMapper(); + var asyncToolFilterFactory = this.toolFilterFactory != null + ? McpAsyncToolFilterFactory.fromSync(this.toolFilterFactory) : null; var asyncServer = new McpAsyncServer(this.transportProvider, mapper, asyncFeatures, this.requestTimeout, - this.uriTemplateManagerFactory); + this.uriTemplateManagerFactory, asyncToolFilterFactory); return new McpSyncServer(asyncServer); } diff --git a/mcp/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java b/mcp/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java index 122857a..ce4b97c 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java +++ b/mcp/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java @@ -238,7 +238,13 @@ public class McpServerFeatures { * connected client. The second arguments is a map of tool arguments. */ public record AsyncToolSpecification(McpSchema.Tool tool, + BiFunction<McpAsyncServerExchange, RequestWithContext<McpSchema.CallToolRequest>, Mono<McpSchema.CallToolResult>> call, + SyncToolSpecification syncTool) { + + public AsyncToolSpecification(McpSchema.Tool tool, BiFunction<McpAsyncServerExchange, RequestWithContext<McpSchema.CallToolRequest>, Mono<McpSchema.CallToolResult>> call) { + this(tool, call, null); + } static AsyncToolSpecification fromSync(SyncToolSpecification tool) { // FIXME: This is temporary, proper validation should be implemented @@ -248,7 +254,8 @@ public class McpServerFeatures { return new AsyncToolSpecification(tool.tool(), (exchange, reqWithContext) -> Mono .fromCallable(() -> tool.call().apply(new McpSyncServerExchange(exchange), reqWithContext)) - .subscribeOn(Schedulers.boundedElastic())); + .subscribeOn(Schedulers.boundedElastic()), + tool); } } diff --git a/mcp/src/main/java/io/modelcontextprotocol/util/McpAsyncToolFilterFactory.java b/mcp/src/main/java/io/modelcontextprotocol/util/McpAsyncToolFilterFactory.java new file mode 100644 index 0000000..831ca1a --- /dev/null +++ b/mcp/src/main/java/io/modelcontextprotocol/util/McpAsyncToolFilterFactory.java @@ -0,0 +1,27 @@ +/* + * Copyright 2025 - 2025 the original author or authors. + */ +package io.modelcontextprotocol.util; + +import java.util.function.Predicate; + +import io.modelcontextprotocol.server.McpAsyncServerExchange; +import io.modelcontextprotocol.server.McpServerFeatures.AsyncToolSpecification; +import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; +import io.modelcontextprotocol.server.McpSyncServerExchange; +import io.modelcontextprotocol.spec.McpContext; + +public interface McpAsyncToolFilterFactory { + + public Predicate<AsyncToolSpecification> createFilter(McpAsyncServerExchange exchange, Object params, + McpContext mcpContext); + + public static McpAsyncToolFilterFactory fromSync(McpSyncToolFilterFactory syncToolFilterFactory) { + return (exchange, params, mcpContext) -> { + Predicate<SyncToolSpecification> syncFilter = syncToolFilterFactory + .createFilter(new McpSyncServerExchange(exchange), params, mcpContext); + return asyncToolSpec -> syncFilter.test(asyncToolSpec.syncTool()); + }; + } + +} diff --git a/mcp/src/main/java/io/modelcontextprotocol/util/McpSyncToolFilterFactory.java b/mcp/src/main/java/io/modelcontextprotocol/util/McpSyncToolFilterFactory.java new file mode 100644 index 0000000..b28c0a0 --- /dev/null +++ b/mcp/src/main/java/io/modelcontextprotocol/util/McpSyncToolFilterFactory.java @@ -0,0 +1,17 @@ +/* + * Copyright 2025 - 2025 the original author or authors. + */ +package io.modelcontextprotocol.util; + +import java.util.function.Predicate; + +import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; +import io.modelcontextprotocol.server.McpSyncServerExchange; +import io.modelcontextprotocol.spec.McpContext; + +public interface McpSyncToolFilterFactory { + + public Predicate<SyncToolSpecification> createFilter(McpSyncServerExchange exchange, Object params, + McpContext mcpContext); + +}Reacted by He-Pin(kerr)I also have this need. There should either be the ability to add your own request handlers, or make the handler methods of the server protected so you can subclass the server and modify the behaviour
Reacted by songyuwongI think this would be addressed in #578 and I suggest the discussion move to that issue.
- addedenhancementNew feature or requestNew feature or requestduplicateThis issue or pull request already existsThis issue or pull request already exists
on Feb 18, 2026
Metadata
Metadata
Assignees
Labels
duplicateThis issue or pull request already existsThis issue or pull request already existsenhancementNew feature or requestNew feature or requestfeature/repositories
Motivation:
We need to filter the
toolandresources. When the client requests a tool list or a resource list, we need to filter out the tools/resources which is not visible to some users.It would be nice to set
filter* toMcp*Servedr