Skip to content

Add support filter tool/resources #301

Description

@He-Pin

Motivation:
We need to filter the tool and resources. 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* to Mcp*Servedr

Activity

  1. jahlbornG commented on Jul 2, 2025

    @jahlbornG

    yes, we have this need as well.

  2. jahlbornG commented on Jul 9, 2025

    @jahlbornG

    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);
    +
    +}
    
    
  3. garpinc commented on Aug 27, 2025

    @garpinc

    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

  4. chemicL commented on Feb 18, 2026

    @chemicL
    Member

    I think this would be addressed in #578 and I suggest the discussion move to that issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions