Remove server back-references from protocol DTO types (#1345)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Co-authored-by: Stephen Toub <stoub@microsoft.com>
This commit is contained in:
Copilot 2026-02-23 14:17:54 -05:00 • committed by GitHub
commit 74622d0c9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -398,6 +398,105 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
log.Exception.Message.Contains("Ensure that AddAuthorizationFilters() is called"));
}
[Fact]
public async Task ListTools_WithHandlerAndNullCollection_AllToolsVisible()
{
// When ToolCollection is null (custom handler only), the auth filter can't look up
// primitives in the collection and should not filter any tools.
await using var app = await StartServerWithAuth(builder =>
builder.WithListToolsHandler(static (_, _) => ValueTask.FromResult(new ListToolsResult
{
Tools = [new Tool { Name = "custom_tool" }]
})));
var client = await ConnectAsync();
var tools = await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
// Tool from custom handler (not in ToolCollection) should be visible even to anonymous users
Assert.Single(tools);
Assert.Equal("custom_tool", tools[0].Name);
}
[Fact]
public async Task ListTools_WithMixedCollectionAndHandler_HandlerToolsNotFiltered()
{
// Tools in the ToolCollection are filtered based on auth metadata.
// Tools returned only from a custom handler (not in ToolCollection) are not filtered.
await using var app = await StartServerWithAuth(builder =>
{
builder.WithTools<AuthorizationTestTools>();
builder.WithListToolsHandler(static (_, _) => ValueTask.FromResult(new ListToolsResult
{
Tools = [new Tool { Name = "handler_tool" }]
}));
});
var client = await ConnectAsync();
var tools = await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
// Anonymous user: anonymous_tool from collection + handler_tool (not in collection, so not filtered)
Assert.Equal(2, tools.Count);
var toolNames = tools.Select(t => t.Name).OrderBy(n => n).ToList();
Assert.Equal(["anonymous_tool", "handler_tool"], toolNames);
}
[Fact]
public async Task ListPrompts_WithHandlerAndNullCollection_AllPromptsVisible()
{
// When PromptCollection is null (custom handler only), the auth filter can't look up
// primitives in the collection and should not filter any prompts.
await using var app = await StartServerWithAuth(builder =>
builder.WithListPromptsHandler(static (_, _) => ValueTask.FromResult(new ListPromptsResult
{
Prompts = [new Prompt { Name = "custom_prompt" }]
})));
var client = await ConnectAsync();
var prompts = await client.ListPromptsAsync(cancellationToken: TestContext.Current.CancellationToken);
// Prompt from custom handler (not in PromptCollection) should be visible even to anonymous users
Assert.Single(prompts);
Assert.Equal("custom_prompt", prompts[0].Name);
}
[Fact]
public async Task ListResources_WithHandlerAndNullCollection_AllResourcesVisible()
{
// When ResourceCollection is null (custom handler only), the auth filter can't look up
// primitives in the collection and should not filter any resources.
await using var app = await StartServerWithAuth(builder =>
builder.WithListResourcesHandler(static (_, _) => ValueTask.FromResult(new ListResourcesResult
{
Resources = [new Resource { Name = "custom_resource", Uri = "resource://custom" }]
})));
var client = await ConnectAsync();
var resources = await client.ListResourcesAsync(cancellationToken: TestContext.Current.CancellationToken);
// Resource from custom handler (not in ResourceCollection) should be visible even to anonymous users
Assert.Single(resources);
Assert.Equal("resource://custom", resources[0].Uri);
}
[Fact]
public async Task ListResourceTemplates_WithHandlerAndNullCollection_AllResourceTemplatesVisible()
{
// When ResourceCollection is null (custom handler only), the auth filter can't look up
// primitives in the collection and should not filter any resource templates.
await using var app = await StartServerWithAuth(builder =>
builder.WithListResourceTemplatesHandler(static (_, _) => ValueTask.FromResult(new ListResourceTemplatesResult
{
ResourceTemplates = [new ResourceTemplate { Name = "custom_template", UriTemplate = "resource://custom/{id}" }]
})));
var client = await ConnectAsync();
var templates = await client.ListResourceTemplatesAsync(cancellationToken: TestContext.Current.CancellationToken);
// Template from custom handler (not in ResourceCollection) should be visible even to anonymous users
Assert.Single(templates);
Assert.Equal("resource://custom/{id}", templates[0].UriTemplate);
}
private async Task<WebApplication> StartServerWithAuth(Action<IMcpServerBuilder> configure, string? userName = null, params string[] roles)
{
var mcpServerBuilder = Builder.Services.AddMcpServer().WithHttpTransport().AddAuthorizationFilters();