@@ -12,7 +12,7 @@ import type {
1212} from "@roo-code/types"
1313
1414import { addCustomInstructions } from "../core/prompts/sections/custom-instructions"
15- import { matchesGlobPattern , matchesAnyPattern } from "./pattern-matching"
15+ import { matchesGlobPattern , matchesAnyPattern , findMostSpecificMatchingPattern } from "./pattern-matching"
1616
1717import { EXPERIMENT_IDS } from "./experiments"
1818import { TOOL_GROUPS , ALWAYS_AVAILABLE_TOOLS } from "./tools"
@@ -229,44 +229,101 @@ export function isServerAllowedForMode(
229229 return true
230230}
231231
232+ /**
233+ * Resolve tool access using pattern specificity logic
234+ * Exact patterns (no wildcards) always win over wildcard patterns
235+ * Among wildcard patterns, more specific patterns win
236+ * @param serverName - Name of the server
237+ * @param toolName - Name of the tool
238+ * @param allowPatterns - Array of allow patterns
239+ * @param blockPatterns - Array of block patterns
240+ * @returns boolean indicating if tool should be allowed
241+ */
242+ function resolveToolAccess (
243+ serverName : string ,
244+ toolName : string ,
245+ allowPatterns : { serverName : string ; toolName : string } [ ] ,
246+ blockPatterns : { serverName : string ; toolName : string } [ ] ,
247+ ) : boolean {
248+ // Collect all matching tool patterns from both allow and block lists
249+ const matchingAllowPatterns : string [ ] = [ ]
250+ const matchingBlockPatterns : string [ ] = [ ]
251+
252+ // Check allow patterns
253+ for ( const pattern of allowPatterns ) {
254+ const serverMatches = matchesGlobPattern ( serverName , pattern . serverName )
255+ const toolMatches = matchesGlobPattern ( toolName , pattern . toolName )
256+ if ( serverMatches && toolMatches ) {
257+ // Store the tool pattern (not combined) for specificity comparison
258+ matchingAllowPatterns . push ( pattern . toolName )
259+ }
260+ }
261+
262+ // Check block patterns
263+ for ( const pattern of blockPatterns ) {
264+ const serverMatches = matchesGlobPattern ( serverName , pattern . serverName )
265+ const toolMatches = matchesGlobPattern ( toolName , pattern . toolName )
266+ if ( serverMatches && toolMatches ) {
267+ // Store the tool pattern (not combined) for specificity comparison
268+ matchingBlockPatterns . push ( pattern . toolName )
269+ }
270+ }
271+
272+ // If no patterns match, allow by default
273+ if ( matchingAllowPatterns . length === 0 && matchingBlockPatterns . length === 0 ) {
274+ return true
275+ }
276+
277+ // Find the most specific pattern from all matching patterns
278+ const allMatchingPatterns = [ ...matchingAllowPatterns , ...matchingBlockPatterns ]
279+ const mostSpecificPattern = findMostSpecificMatchingPattern ( toolName , allMatchingPatterns )
280+
281+ if ( ! mostSpecificPattern ) {
282+ return true // Default to allow if no pattern found (shouldn't happen)
283+ }
284+
285+ // Determine if the most specific pattern is from allow or block list
286+ return matchingAllowPatterns . includes ( mostSpecificPattern )
287+ }
288+
232289export function isToolAllowedForModeAndServer (
233290 serverName : string ,
234291 toolName : string ,
235292 restrictions : McpRestrictions ,
236293) : boolean {
237- // If allowedTools is defined, tool must match at least one entry
238- if ( restrictions . allowedTools ) {
239- // Filter out empty entries before checking
240- const validAllowedTools = restrictions . allowedTools . filter ( ( t ) => t . serverName ?. trim ( ) && t . toolName ?. trim ( ) )
241- if ( validAllowedTools . length > 0 ) {
242- const isAllowed = validAllowedTools . some ( ( t ) => {
243- // Check if server name matches (with pattern support)
244- const serverMatches = matchesAnyPattern ( serverName , [ t . serverName ] )
245- // Check if tool name matches (with pattern support)
246- const toolMatches = matchesAnyPattern ( toolName , [ t . toolName ] )
247- return serverMatches && toolMatches
248- } )
249- if ( ! isAllowed ) return false
250- }
294+ // Get valid allow and block patterns
295+ const validAllowedTools = ( restrictions . allowedTools || [ ] ) . filter (
296+ ( t ) => t . serverName ?. trim ( ) && t . toolName ?. trim ( ) ,
297+ )
298+ const validDisallowedTools = ( restrictions . disallowedTools || [ ] ) . filter (
299+ ( t ) => t . serverName ?. trim ( ) && t . toolName ?. trim ( ) ,
300+ )
301+
302+ // If there are no restrictions at all, allow the tool
303+ if ( validAllowedTools . length === 0 && validDisallowedTools . length === 0 ) {
304+ return true
251305 }
252306
253- // If disallowedTools is defined, tool must not match any entry
254- if ( restrictions . disallowedTools ) {
255- // Filter out empty entries before checking
256- const validDisallowedTools = restrictions . disallowedTools . filter (
257- ( t ) => t . serverName ?. trim ( ) && t . toolName ?. trim ( ) ,
258- )
259- const isDisallowed = validDisallowedTools . some ( ( t ) => {
260- // Check if server name matches (with pattern support)
261- const serverMatches = matchesAnyPattern ( serverName , [ t . serverName ] )
262- // Check if tool name matches (with pattern support)
263- const toolMatches = matchesAnyPattern ( toolName , [ t . toolName ] )
307+ // If only allowedTools is defined, tool must match at least one entry
308+ if ( validAllowedTools . length > 0 && validDisallowedTools . length === 0 ) {
309+ return validAllowedTools . some ( ( t ) => {
310+ const serverMatches = matchesGlobPattern ( serverName , t . serverName )
311+ const toolMatches = matchesGlobPattern ( toolName , t . toolName )
264312 return serverMatches && toolMatches
265313 } )
266- if ( isDisallowed ) return false
267314 }
268315
269- return true
316+ // If only disallowedTools is defined, tool must not match any entry
317+ if ( validAllowedTools . length === 0 && validDisallowedTools . length > 0 ) {
318+ return ! validDisallowedTools . some ( ( t ) => {
319+ const serverMatches = matchesGlobPattern ( serverName , t . serverName )
320+ const toolMatches = matchesGlobPattern ( toolName , t . toolName )
321+ return serverMatches && toolMatches
322+ } )
323+ }
324+
325+ // If both allowedTools and disallowedTools are defined, use pattern specificity resolution
326+ return resolveToolAccess ( serverName , toolName , validAllowedTools , validDisallowedTools )
270327}
271328
272329// Custom error class for file restrictions
0 commit comments