@@ -366,6 +366,101 @@ func TestDirectModeHandler_DestructiveToolNeedsDestructivePermission(t *testing.
366366 assert .Contains (t , result .Content [0 ].(mcp.TextContent ).Text , "destructive" )
367367}
368368
369+ func TestRequiredPermissionForDirectTool_MapsAnnotationsToAuthPermissions (t * testing.T ) {
370+ readOnly := true
371+ write := false
372+ destructive := true
373+
374+ tests := []struct {
375+ name string
376+ annotations * config.ToolAnnotations
377+ want string
378+ }{
379+ {
380+ name : "nil annotations default to read" ,
381+ want : auth .PermRead ,
382+ },
383+ {
384+ name : "read only hint maps to read" ,
385+ annotations : & config.ToolAnnotations {
386+ ReadOnlyHint : & readOnly ,
387+ },
388+ want : auth .PermRead ,
389+ },
390+ {
391+ name : "read only false maps to write" ,
392+ annotations : & config.ToolAnnotations {
393+ ReadOnlyHint : & write ,
394+ },
395+ want : auth .PermWrite ,
396+ },
397+ {
398+ name : "destructive hint maps to destructive" ,
399+ annotations : & config.ToolAnnotations {
400+ DestructiveHint : & destructive ,
401+ },
402+ want : auth .PermDestructive ,
403+ },
404+ {
405+ name : "destructive hint takes precedence over read only hint" ,
406+ annotations : & config.ToolAnnotations {
407+ ReadOnlyHint : & readOnly ,
408+ DestructiveHint : & destructive ,
409+ },
410+ want : auth .PermDestructive ,
411+ },
412+ }
413+
414+ for _ , tt := range tests {
415+ t .Run (tt .name , func (t * testing.T ) {
416+ assert .Equal (t , tt .want , requiredPermissionForDirectTool (tt .annotations ))
417+ })
418+ }
419+ }
420+
421+ func TestSetDirectToolPermissions_DefensivelyCopiesMap (t * testing.T ) {
422+ proxy := & MCPProxyServer {}
423+ toolName := FormatDirectToolName ("github" , "get_issue" )
424+ perms := map [string ]string {
425+ toolName : auth .PermRead ,
426+ }
427+
428+ proxy .setDirectToolPermissions (perms )
429+ perms [toolName ] = auth .PermDestructive
430+
431+ got , ok := proxy .lookupDirectToolPermission (toolName )
432+ require .True (t , ok )
433+ assert .Equal (t , auth .PermRead , got )
434+ }
435+
436+ func TestFilterDirectModeToolsForAuth_DoesNotMutateInputSlice (t * testing.T ) {
437+ proxy := & MCPProxyServer {}
438+ allowed := FormatDirectToolName ("github" , "get_issue" )
439+ denied := FormatDirectToolName ("gitlab" , "get_issue" )
440+ tools := []mcp.Tool {
441+ {Name : allowed },
442+ {Name : denied },
443+ }
444+ original := append ([]mcp.Tool (nil ), tools ... )
445+
446+ proxy .setDirectToolPermissions (map [string ]string {
447+ allowed : auth .PermRead ,
448+ denied : auth .PermRead ,
449+ })
450+
451+ ctx := auth .WithAuthContext (context .Background (), & auth.AuthContext {
452+ Type : auth .AuthTypeAgent ,
453+ AgentName : "test-agent" ,
454+ AllowedServers : []string {"github" },
455+ Permissions : []string {auth .PermRead },
456+ })
457+
458+ filtered := proxy .filterDirectModeToolsForAuth (ctx , tools )
459+
460+ assert .Equal (t , []string {allowed }, directToolNamesForTest (filtered ))
461+ assert .Equal (t , original , tools )
462+ }
463+
369464func TestFilterDirectModeToolsForAuth_AgentServerAndPermissionScope (t * testing.T ) {
370465 proxy := & MCPProxyServer {}
371466
0 commit comments