@@ -398,6 +398,105 @@ log.Exception is InvalidOperationException &&
398398 log . Exception . Message . Contains ( "Ensure that AddAuthorizationFilters() is called" ) ) ;
399399 }
400400
401+ [ Fact ]
402+ public async Task ListTools_WithHandlerAndNullCollection_AllToolsVisible ( )
403+ {
404+ // When ToolCollection is null (custom handler only), the auth filter can't look up
405+ // primitives in the collection and should not filter any tools.
406+ await using var app = await StartServerWithAuth ( builder =>
407+ builder . WithListToolsHandler ( static ( _ , _ ) => ValueTask . FromResult ( new ListToolsResult
408+ {
409+ Tools = [ new Tool { Name = "custom_tool" } ]
410+ } ) ) ) ;
411+
412+ var client = await ConnectAsync ( ) ;
413+ var tools = await client . ListToolsAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
414+
415+ // Tool from custom handler (not in ToolCollection) should be visible even to anonymous users
416+ Assert . Single ( tools ) ;
417+ Assert . Equal ( "custom_tool" , tools [ 0 ] . Name ) ;
418+ }
419+
420+ [ Fact ]
421+ public async Task ListTools_WithMixedCollectionAndHandler_HandlerToolsNotFiltered ( )
422+ {
423+ // Tools in the ToolCollection are filtered based on auth metadata.
424+ // Tools returned only from a custom handler (not in ToolCollection) are not filtered.
425+ await using var app = await StartServerWithAuth ( builder =>
426+ {
427+ builder . WithTools < AuthorizationTestTools > ( ) ;
428+ builder . WithListToolsHandler ( static ( _ , _ ) => ValueTask . FromResult ( new ListToolsResult
429+ {
430+ Tools = [ new Tool { Name = "handler_tool" } ]
431+ } ) ) ;
432+ } ) ;
433+
434+ var client = await ConnectAsync ( ) ;
435+ var tools = await client . ListToolsAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
436+
437+ // Anonymous user: anonymous_tool from collection + handler_tool (not in collection, so not filtered)
438+ Assert . Equal ( 2 , tools . Count ) ;
439+ var toolNames = tools . Select ( t => t . Name ) . OrderBy ( n => n ) . ToList ( ) ;
440+ Assert . Equal ( [ "anonymous_tool" , "handler_tool" ] , toolNames ) ;
441+ }
442+
443+ [ Fact ]
444+ public async Task ListPrompts_WithHandlerAndNullCollection_AllPromptsVisible ( )
445+ {
446+ // When PromptCollection is null (custom handler only), the auth filter can't look up
447+ // primitives in the collection and should not filter any prompts.
448+ await using var app = await StartServerWithAuth ( builder =>
449+ builder . WithListPromptsHandler ( static ( _ , _ ) => ValueTask . FromResult ( new ListPromptsResult
450+ {
451+ Prompts = [ new Prompt { Name = "custom_prompt" } ]
452+ } ) ) ) ;
453+
454+ var client = await ConnectAsync ( ) ;
455+ var prompts = await client . ListPromptsAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
456+
457+ // Prompt from custom handler (not in PromptCollection) should be visible even to anonymous users
458+ Assert . Single ( prompts ) ;
459+ Assert . Equal ( "custom_prompt" , prompts [ 0 ] . Name ) ;
460+ }
461+
462+ [ Fact ]
463+ public async Task ListResources_WithHandlerAndNullCollection_AllResourcesVisible ( )
464+ {
465+ // When ResourceCollection is null (custom handler only), the auth filter can't look up
466+ // primitives in the collection and should not filter any resources.
467+ await using var app = await StartServerWithAuth ( builder =>
468+ builder . WithListResourcesHandler ( static ( _ , _ ) => ValueTask . FromResult ( new ListResourcesResult
469+ {
470+ Resources = [ new Resource { Name = "custom_resource" , Uri = "resource://custom" } ]
471+ } ) ) ) ;
472+
473+ var client = await ConnectAsync ( ) ;
474+ var resources = await client . ListResourcesAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
475+
476+ // Resource from custom handler (not in ResourceCollection) should be visible even to anonymous users
477+ Assert . Single ( resources ) ;
478+ Assert . Equal ( "resource://custom" , resources [ 0 ] . Uri ) ;
479+ }
480+
481+ [ Fact ]
482+ public async Task ListResourceTemplates_WithHandlerAndNullCollection_AllResourceTemplatesVisible ( )
483+ {
484+ // When ResourceCollection is null (custom handler only), the auth filter can't look up
485+ // primitives in the collection and should not filter any resource templates.
486+ await using var app = await StartServerWithAuth ( builder =>
487+ builder . WithListResourceTemplatesHandler ( static ( _ , _ ) => ValueTask . FromResult ( new ListResourceTemplatesResult
488+ {
489+ ResourceTemplates = [ new ResourceTemplate { Name = "custom_template" , UriTemplate = "resource://custom/{id}" } ]
490+ } ) ) ) ;
491+
492+ var client = await ConnectAsync ( ) ;
493+ var templates = await client . ListResourceTemplatesAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
494+
495+ // Template from custom handler (not in ResourceCollection) should be visible even to anonymous users
496+ Assert . Single ( templates ) ;
497+ Assert . Equal ( "resource://custom/{id}" , templates [ 0 ] . UriTemplate ) ;
498+ }
499+
401500 private async Task < WebApplication > StartServerWithAuth ( Action < IMcpServerBuilder > configure , string ? userName = null , params string [ ] roles )
402501 {
403502 var mcpServerBuilder = Builder . Services . AddMcpServer ( ) . WithHttpTransport ( ) . AddAuthorizationFilters ( ) ;
0 commit comments