Hello,

i have explored a bit the dynamic tool example but it seems that tool are like a "global" concept where i can add and remove tool based on "something".

So if i understand correctly the usage is rather limited, as i can basically enable or disable a tool based on a certain condition, but not on who is executing the call.

Now, if i have a user that connect with a oauth set of grant, it will be cool to filter the tool based on his role/permission set.

At the moment what happen is that i have 2 tool toolA -> all toolB -> user with a specific grant

when user a user not form group B connect, he can ask the question that trigger the tool and then get an exception "you can't use tool b". wouldn't be better to disable the tool so the agent don't even try to invoke it? save token, allow fallback for other tool/agent_logic to be executed?

Another option is to split mcp server based on the "grant" but seems a lot of 'partitioning' that sometimes isn't even necessary and create duplication (Same request model, same db, some logic etc etc)

Apologize for my english, hope my suggestion is clear enough (reasonable or not, it's another matter)

Comment From: ilayaperumalg

Hi @matteo-rama, Thanks for creating the issue.

On MCP tooling, we have recently introduced custom MCP tool filtering by having a custom implementation of McpToolFilter. Please read here for more information on this. Does this help solving your case?

Comment From: matteo-rama

it's actually intresting, do you have a full example where i can see how to use in conjuncton with McpConnectionInfo?

i guess i should add some meta data on mcconnectioninfo somehow inside servlet filter, but autowire doesn't make much sense to me at this point isn't it?

how should i get it into a servlet filter, to populate it with some useful insight to do later the check?

thanks

Comment From: quaff

On MCP tooling, we have recently introduced custom MCP tool filtering by having a custom implementation of McpToolFilter. Please read here for more information on this. Does this help solving your case?

@ilayaperumalg I found that McpToolFilter is applied at client side, I suggest there is an equivalent at server side, we can filter tools/list result base on context such as Spring Security's SecurityContext:

    @Bean
    McpToolFilter mcpToolFilter() {
        return (McpConnectionInfo mcpConnectionInfo, McpSchema.Tool tool) -> {
            Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
            return hasPermission(authorities, tool);
        };
    }

Comment From: abssyyy

@matteo-rama Thanks for the discussion! A simple way to filter tools dynamically before the AI conversation, based on user input and permissions, is to inherit DefaultChatClientRequestSpec or implement ChatClient and override the toolCallbacks method. This reduces prompt tokens and prevents unauthorized tool calls.

Idea

  • Get User Input: Use getUserText() to access the user query.
  • Embedding Filter: Compute similarity between user input and tool descriptions using an EmbeddingModel.
  • Permission Filter: Check user roles via SecurityContextHolder (e.g., allow toolB only for ROLE_GROUP_B).
  • Load Filtered Tools: Add only relevant and authorized tools to toolCallbacks.

Pseudocode

class CustomChatClientRequestSpec extends org.springframework.ai.chat.client.DefaultChatClientRequestSpec {
    EmbeddingModel embeddingModel;

    CustomChatClientRequestSpec(DefaultChatClientRequestSpec spec, EmbeddingModel embeddingModel) {
        super(spec);
        this.embeddingModel = embeddingModel;
    }

    @Override
    ChatClientRequestSpec toolCallbacks(ToolCallbackProvider... providers) {
        List<ToolCallback> filteredCallbacks = new ArrayList<>();
        String userInput = this.userText();
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        for (ToolCallbackProvider provider : providers) {
            for (ToolCallback callback : provider.getToolCallbacks()) {
                ToolDefinition toolDef = callback.getToolDefinition();

                // Permission check
                if (toolDef.name().equals("toolB") && auth != null) {
                    if (!auth.getAuthorities().contains("ROLE_GROUP_B")) continue;
                }

                // Embedding filter
                if (userInput != null) {
                    double similarity = embeddingModel.computeSimilarity(userInput, toolDef.description());
                    if (similarity > 0.8) filteredCallbacks.add(callback);
                } else {
                    filteredCallbacks.add(callback);
                }
            }
        }
        this.toolCallbacks.addAll(filteredCallbacks);
        return this;
    }
}

Drawbacks

  • Order Issue: If toolCallbacks is called before user(), getUserText() may be null, requiring delayed filtering.
  • Performance: Embedding calculations can be slow; caching tool embeddings helps.
  • Client-Side Only: Filters on client side; server-side filtering.

Comment From: markpollack

@matteo-rama regarding "Now, if i have a user that connect with a oauth set of grant, it will be cool to filter the tool based on his role/permission set."

This is now possible - at least on the web-mvc stack. I now realize I forgot to add this information into the release blog for 1.1 M1. I've updated the blog post here.

@Kehrlann what are the next steps to showcase all this goodness?