@@ -693,18 +693,31 @@ impl Agents {
693693
694694 /// Returns a label to describe the permission status for a given tool.
695695 pub fn display_label ( & self , tool_name : & str , origin : & ToolOrigin ) -> String {
696+ use crate :: util:: pattern_matching:: matches_any_pattern;
697+
696698 let tool_trusted = self . get_active ( ) . is_some_and ( |a| {
699+ if matches ! ( origin, & ToolOrigin :: Native ) {
700+ return matches_any_pattern ( & a. allowed_tools , tool_name) ;
701+ }
702+
697703 a. allowed_tools . iter ( ) . any ( |name| {
698- // Here the tool names can take the following forms:
699- // - @{server_name}{delimiter}{tool_name}
700- // - native_tool_name
701- name == tool_name && matches ! ( origin, & ToolOrigin :: Native )
702- || name. strip_prefix ( "@" ) . is_some_and ( |remainder| {
703- remainder
704- . split_once ( MCP_SERVER_TOOL_DELIMITER )
705- . is_some_and ( |( _left, right) | right == tool_name)
706- || remainder == <ToolOrigin as Borrow < str > >:: borrow ( origin)
707- } )
704+ name. strip_prefix ( "@" ) . is_some_and ( |remainder| {
705+ remainder
706+ . split_once ( MCP_SERVER_TOOL_DELIMITER )
707+ . is_some_and ( |( _left, right) | right == tool_name)
708+ || remainder == <ToolOrigin as Borrow < str > >:: borrow ( origin)
709+ } ) || {
710+ if let Some ( server_name) = name. strip_prefix ( "@" ) . and_then ( |s| s. split ( '/' ) . next ( ) ) {
711+ if server_name == <ToolOrigin as Borrow < str > >:: borrow ( origin) {
712+ let tool_pattern = format ! ( "@{}/{}" , server_name, tool_name) ;
713+ matches_any_pattern ( & a. allowed_tools , & tool_pattern)
714+ } else {
715+ false
716+ }
717+ } else {
718+ false
719+ }
720+ }
708721 } )
709722 } ) ;
710723
@@ -942,4 +955,108 @@ mod tests {
942955 assert ! ( validate_agent_name( "invalid!" ) . is_err( ) ) ;
943956 assert ! ( validate_agent_name( "invalid space" ) . is_err( ) ) ;
944957 }
958+
959+ #[ test]
960+ fn test_display_label_no_active_agent ( ) {
961+ let agents = Agents :: default ( ) ;
962+
963+ let label = agents. display_label ( "fs_read" , & ToolOrigin :: Native ) ;
964+ // With no active agent, it should fall back to default permissions
965+ // fs_read has a default of "trusted"
966+ assert ! ( label. contains( "trusted" ) , "fs_read should show default trusted permission, instead found: {}" , label) ;
967+ }
968+
969+ #[ test]
970+ fn test_display_label_trust_all_tools ( ) {
971+ let mut agents = Agents :: default ( ) ;
972+ agents. trust_all_tools = true ;
973+
974+ // Should be trusted even if not in allowed_tools
975+ let label = agents. display_label ( "random_tool" , & ToolOrigin :: Native ) ;
976+ assert ! ( label. contains( "trusted" ) , "trust_all_tools should make everything trusted, instead found: {}" , label) ;
977+ }
978+
979+ #[ test]
980+ fn test_display_label_default_permissions ( ) {
981+ let agents = Agents :: default ( ) ;
982+
983+ // Test default permissions for known tools
984+ let fs_read_label = agents. display_label ( "fs_read" , & ToolOrigin :: Native ) ;
985+ assert ! ( fs_read_label. contains( "trusted" ) , "fs_read should be trusted by default, instead found: {}" , fs_read_label) ;
986+
987+ let fs_write_label = agents. display_label ( "fs_write" , & ToolOrigin :: Native ) ;
988+ assert ! ( fs_write_label. contains( "not trusted" ) , "fs_write should not be trusted by default, instead found: {}" , fs_write_label) ;
989+
990+ let execute_bash_label = agents. display_label ( "execute_bash" , & ToolOrigin :: Native ) ;
991+ assert ! ( execute_bash_label. contains( "read-only" ) , "execute_bash should show read-only by default, instead found: {}" , execute_bash_label) ;
992+ }
993+
994+ #[ test]
995+ fn test_display_label_comprehensive_patterns ( ) {
996+ let mut agents = Agents :: default ( ) ;
997+
998+ // Create agent with all types of patterns
999+ let mut allowed_tools = HashSet :: new ( ) ;
1000+ // Native exact match
1001+ allowed_tools. insert ( "fs_read" . to_string ( ) ) ;
1002+ // Native wildcard
1003+ allowed_tools. insert ( "execute_*" . to_string ( ) ) ;
1004+ // MCP server exact (allows all tools from that server)
1005+ allowed_tools. insert ( "@server1" . to_string ( ) ) ;
1006+ // MCP tool exact
1007+ allowed_tools. insert ( "@server2/specific_tool" . to_string ( ) ) ;
1008+ // MCP tool wildcard
1009+ allowed_tools. insert ( "@server3/tool_*" . to_string ( ) ) ;
1010+
1011+ let agent = Agent {
1012+ schema : "test" . to_string ( ) ,
1013+ name : "test-agent" . to_string ( ) ,
1014+ description : None ,
1015+ prompt : None ,
1016+ mcp_servers : Default :: default ( ) ,
1017+ tools : Vec :: new ( ) ,
1018+ tool_aliases : Default :: default ( ) ,
1019+ allowed_tools,
1020+ tools_settings : Default :: default ( ) ,
1021+ resources : Vec :: new ( ) ,
1022+ hooks : Default :: default ( ) ,
1023+ use_legacy_mcp_json : false ,
1024+ path : None ,
1025+ } ;
1026+
1027+ agents. agents . insert ( "test-agent" . to_string ( ) , agent) ;
1028+ agents. active_idx = "test-agent" . to_string ( ) ;
1029+
1030+ // Test 1: Native exact match
1031+ let label = agents. display_label ( "fs_read" , & ToolOrigin :: Native ) ;
1032+ assert ! ( label. contains( "trusted" ) , "fs_read should be trusted (exact match), instead found: {}" , label) ;
1033+
1034+ // Test 2: Native wildcard match
1035+ let label = agents. display_label ( "execute_bash" , & ToolOrigin :: Native ) ;
1036+ assert ! ( label. contains( "trusted" ) , "execute_bash should match execute_* pattern, instead found: {}" , label) ;
1037+
1038+ // Test 3: Native no match
1039+ let label = agents. display_label ( "fs_write" , & ToolOrigin :: Native ) ;
1040+ assert ! ( !label. contains( "trusted" ) || label. contains( "not trusted" ) , "fs_write should not be trusted, instead found: {}" , label) ;
1041+
1042+ // Test 4: MCP server exact match (allows any tool from server1)
1043+ let label = agents. display_label ( "any_tool" , & ToolOrigin :: McpServer ( "server1" . to_string ( ) ) ) ;
1044+ assert ! ( label. contains( "trusted" ) , "Server-level permission should allow any tool, instead found: {}" , label) ;
1045+
1046+ // Test 5: MCP tool exact match
1047+ let label = agents. display_label ( "specific_tool" , & ToolOrigin :: McpServer ( "server2" . to_string ( ) ) ) ;
1048+ assert ! ( label. contains( "trusted" ) , "Exact MCP tool should be trusted, instead found: {}" , label) ;
1049+
1050+ // Test 6: MCP tool wildcard match
1051+ let label = agents. display_label ( "tool_read" , & ToolOrigin :: McpServer ( "server3" . to_string ( ) ) ) ;
1052+ assert ! ( label. contains( "trusted" ) , "tool_read should match @server3/tool_* pattern, instead found: {}" , label) ;
1053+
1054+ // Test 7: MCP tool no match
1055+ let label = agents. display_label ( "other_tool" , & ToolOrigin :: McpServer ( "server2" . to_string ( ) ) ) ;
1056+ assert ! ( !label. contains( "trusted" ) || label. contains( "not trusted" ) , "Non-matching MCP tool should not be trusted, instead found: {}" , label) ;
1057+
1058+ // Test 8: MCP server no match
1059+ let label = agents. display_label ( "some_tool" , & ToolOrigin :: McpServer ( "unknown_server" . to_string ( ) ) ) ;
1060+ assert ! ( !label. contains( "trusted" ) || label. contains( "not trusted" ) , "Unknown server should not be trusted, instead found: {}" , label) ;
1061+ }
9451062}
0 commit comments