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
8 changed files with 195 additions and 96 deletions

View file

@ -43,31 +43,39 @@ internal sealed class AuthorizationFilterSetup(IAuthorizationPolicyProvider? pol
private void ConfigureListToolsFilter(McpServerOptions options)
{
options.Filters.Request.ListToolsFilters.Add(next => async (context, cancellationToken) =>
options.Filters.Request.ListToolsFilters.Add(next =>
{
context.Items[AuthorizationFilterInvokedKey] = true;
var toolCollection = options.ToolCollection;
return async (context, cancellationToken) =>
{
context.Items[AuthorizationFilterInvokedKey] = true;
var result = await next(context, cancellationToken);
await FilterAuthorizedItemsAsync(
result.Tools, static tool => tool.McpServerTool,
context.User, context.Services, context);
return result;
var result = await next(context, cancellationToken);
await FilterAuthorizedItemsAsync(
result.Tools, tool => toolCollection is not null && toolCollection.TryGetPrimitive(tool.Name, out var serverTool) ? serverTool : null,
context.User, context.Services, context);
return result;
};
});
}
private static void CheckListToolsFilter(McpServerOptions options)
{
options.Filters.Request.ListToolsFilters.Add(next => async (context, cancellationToken) =>
options.Filters.Request.ListToolsFilters.Add(next =>
{
var result = await next(context, cancellationToken);
if (HasAuthorizationMetadata(result.Tools.Select(static tool => tool.McpServerTool))
&& !context.Items.ContainsKey(AuthorizationFilterInvokedKey))
var toolCollection = options.ToolCollection;
return async (context, cancellationToken) =>
{
throw new InvalidOperationException("Authorization filter was not invoked for tools/list operation, but authorization metadata was found on the tools. Ensure that AddAuthorizationFilters() is called on the IMcpServerBuilder to configure authorization filters.");
}
var result = await next(context, cancellationToken);
return result;
if (HasAuthorizationMetadata(result.Tools.Select(tool => toolCollection is not null && toolCollection.TryGetPrimitive(tool.Name, out var serverTool) ? serverTool : null))
&& !context.Items.ContainsKey(AuthorizationFilterInvokedKey))
{
throw new InvalidOperationException("Authorization filter was not invoked for tools/list operation, but authorization metadata was found on the tools. Ensure that AddAuthorizationFilters() is called on the IMcpServerBuilder to configure authorization filters.");
}
return result;
};
});
}
@ -103,61 +111,77 @@ internal sealed class AuthorizationFilterSetup(IAuthorizationPolicyProvider? pol
private void ConfigureListResourcesFilter(McpServerOptions options)
{
options.Filters.Request.ListResourcesFilters.Add(next => async (context, cancellationToken) =>
options.Filters.Request.ListResourcesFilters.Add(next =>
{
context.Items[AuthorizationFilterInvokedKey] = true;
var resourceCollection = options.ResourceCollection;
return async (context, cancellationToken) =>
{
context.Items[AuthorizationFilterInvokedKey] = true;
var result = await next(context, cancellationToken);
await FilterAuthorizedItemsAsync(
result.Resources, static resource => resource.McpServerResource,
context.User, context.Services, context);
return result;
var result = await next(context, cancellationToken);
await FilterAuthorizedItemsAsync(
result.Resources, resource => resourceCollection is not null && resourceCollection.TryGetPrimitive(resource.Uri, out var serverResource) ? serverResource : null,
context.User, context.Services, context);
return result;
};
});
}
private static void CheckListResourcesFilter(McpServerOptions options)
{
options.Filters.Request.ListResourcesFilters.Add(next => async (context, cancellationToken) =>
options.Filters.Request.ListResourcesFilters.Add(next =>
{
var result = await next(context, cancellationToken);
if (HasAuthorizationMetadata(result.Resources.Select(static resource => resource.McpServerResource))
&& !context.Items.ContainsKey(AuthorizationFilterInvokedKey))
var resourceCollection = options.ResourceCollection;
return async (context, cancellationToken) =>
{
throw new InvalidOperationException("Authorization filter was not invoked for resources/list operation, but authorization metadata was found on the resources. Ensure that AddAuthorizationFilters() is called on the IMcpServerBuilder to configure authorization filters.");
}
var result = await next(context, cancellationToken);
return result;
if (HasAuthorizationMetadata(result.Resources.Select(resource => resourceCollection is not null && resourceCollection.TryGetPrimitive(resource.Uri, out var serverResource) ? serverResource : null))
&& !context.Items.ContainsKey(AuthorizationFilterInvokedKey))
{
throw new InvalidOperationException("Authorization filter was not invoked for resources/list operation, but authorization metadata was found on the resources. Ensure that AddAuthorizationFilters() is called on the IMcpServerBuilder to configure authorization filters.");
}
return result;
};
});
}
private void ConfigureListResourceTemplatesFilter(McpServerOptions options)
{
options.Filters.Request.ListResourceTemplatesFilters.Add(next => async (context, cancellationToken) =>
options.Filters.Request.ListResourceTemplatesFilters.Add(next =>
{
context.Items[AuthorizationFilterInvokedKey] = true;
var resourceCollection = options.ResourceCollection;
return async (context, cancellationToken) =>
{
context.Items[AuthorizationFilterInvokedKey] = true;
var result = await next(context, cancellationToken);
await FilterAuthorizedItemsAsync(
result.ResourceTemplates, static resourceTemplate => resourceTemplate.McpServerResource,
context.User, context.Services, context);
return result;
var result = await next(context, cancellationToken);
await FilterAuthorizedItemsAsync(
result.ResourceTemplates, resourceTemplate => resourceCollection is not null && resourceCollection.TryGetPrimitive(resourceTemplate.UriTemplate, out var serverResource) ? serverResource : null,
context.User, context.Services, context);
return result;
};
});
}
private static void CheckListResourceTemplatesFilter(McpServerOptions options)
{
options.Filters.Request.ListResourceTemplatesFilters.Add(next => async (context, cancellationToken) =>
options.Filters.Request.ListResourceTemplatesFilters.Add(next =>
{
var result = await next(context, cancellationToken);
if (HasAuthorizationMetadata(result.ResourceTemplates.Select(static resourceTemplate => resourceTemplate.McpServerResource))
&& !context.Items.ContainsKey(AuthorizationFilterInvokedKey))
var resourceCollection = options.ResourceCollection;
return async (context, cancellationToken) =>
{
throw new InvalidOperationException("Authorization filter was not invoked for resources/templates/list operation, but authorization metadata was found on the resource templates. Ensure that AddAuthorizationFilters() is called on the IMcpServerBuilder to configure authorization filters.");
}
var result = await next(context, cancellationToken);
return result;
if (HasAuthorizationMetadata(result.ResourceTemplates.Select(resourceTemplate => resourceCollection is not null && resourceCollection.TryGetPrimitive(resourceTemplate.UriTemplate, out var serverResource) ? serverResource : null))
&& !context.Items.ContainsKey(AuthorizationFilterInvokedKey))
{
throw new InvalidOperationException("Authorization filter was not invoked for resources/templates/list operation, but authorization metadata was found on the resource templates. Ensure that AddAuthorizationFilters() is called on the IMcpServerBuilder to configure authorization filters.");
}
return result;
};
});
}
@ -193,31 +217,39 @@ internal sealed class AuthorizationFilterSetup(IAuthorizationPolicyProvider? pol
private void ConfigureListPromptsFilter(McpServerOptions options)
{
options.Filters.Request.ListPromptsFilters.Add(next => async (context, cancellationToken) =>
options.Filters.Request.ListPromptsFilters.Add(next =>
{
context.Items[AuthorizationFilterInvokedKey] = true;
var promptCollection = options.PromptCollection;
return async (context, cancellationToken) =>
{
context.Items[AuthorizationFilterInvokedKey] = true;
var result = await next(context, cancellationToken);
await FilterAuthorizedItemsAsync(
result.Prompts, static prompt => prompt.McpServerPrompt,
context.User, context.Services, context);
return result;
var result = await next(context, cancellationToken);
await FilterAuthorizedItemsAsync(
result.Prompts, prompt => promptCollection is not null && promptCollection.TryGetPrimitive(prompt.Name, out var serverPrompt) ? serverPrompt : null,
context.User, context.Services, context);
return result;
};
});
}
private static void CheckListPromptsFilter(McpServerOptions options)
{
options.Filters.Request.ListPromptsFilters.Add(next => async (context, cancellationToken) =>
options.Filters.Request.ListPromptsFilters.Add(next =>
{
var result = await next(context, cancellationToken);
if (HasAuthorizationMetadata(result.Prompts.Select(static prompt => prompt.McpServerPrompt))
&& !context.Items.ContainsKey(AuthorizationFilterInvokedKey))
var promptCollection = options.PromptCollection;
return async (context, cancellationToken) =>
{
throw new InvalidOperationException("Authorization filter was not invoked for prompts/list operation, but authorization metadata was found on the prompts. Ensure that AddAuthorizationFilters() is called on the IMcpServerBuilder to configure authorization filters.");
}
var result = await next(context, cancellationToken);
return result;
if (HasAuthorizationMetadata(result.Prompts.Select(prompt => promptCollection is not null && promptCollection.TryGetPrimitive(prompt.Name, out var serverPrompt) ? serverPrompt : null))
&& !context.Items.ContainsKey(AuthorizationFilterInvokedKey))
{
throw new InvalidOperationException("Authorization filter was not invoked for prompts/list operation, but authorization metadata was found on the prompts. Ensure that AddAuthorizationFilters() is called on the IMcpServerBuilder to configure authorization filters.");
}
return result;
};
});
}

View file

@ -1,7 +1,6 @@
using System.Diagnostics;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using ModelContextProtocol.Server;
namespace ModelContextProtocol.Protocol;
@ -72,12 +71,6 @@ public sealed class Prompt : IBaseMetadata
[JsonPropertyName("_meta")]
public JsonObject? Meta { get; set; }
/// <summary>
/// Gets or sets the callable server prompt corresponding to this metadata if any.
/// </summary>
[JsonIgnore]
public McpServerPrompt? McpServerPrompt { get; set; }
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DebuggerDisplay
{

View file

@ -2,7 +2,6 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using ModelContextProtocol.Server;
namespace ModelContextProtocol.Protocol;
@ -101,10 +100,4 @@ public sealed class Resource : IBaseMetadata
/// </remarks>
[JsonPropertyName("_meta")]
public JsonObject? Meta { get; set; }
/// <summary>
/// Gets or sets the callable server resource corresponding to this metadata if any.
/// </summary>
[JsonIgnore]
public McpServerResource? McpServerResource { get; set; }
}

View file

@ -1,6 +1,5 @@
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using ModelContextProtocol.Server;
namespace ModelContextProtocol.Protocol;
@ -94,12 +93,6 @@ public sealed class ResourceTemplate : IBaseMetadata
[JsonIgnore]
public bool IsTemplated => UriTemplate.Contains('{');
/// <summary>
/// Gets or sets the callable server resource corresponding to this metadata, if any.
/// </summary>
[JsonIgnore]
public McpServerResource? McpServerResource { get; set; }
/// <summary>Converts the <see cref="ResourceTemplate"/> into a <see cref="Resource"/>.</summary>
/// <returns>A <see cref="Resource"/> if <see cref="IsTemplated"/> is <see langword="false"/>; otherwise, <see langword="null"/>.</returns>
public Resource? AsResource()
@ -119,7 +112,6 @@ public sealed class ResourceTemplate : IBaseMetadata
Annotations = Annotations,
Icons = Icons,
Meta = Meta,
McpServerResource = McpServerResource,
};
}
}

View file

@ -1,4 +1,3 @@
using ModelContextProtocol.Server;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
@ -158,12 +157,6 @@ public sealed class Tool : IBaseMetadata
[JsonPropertyName("_meta")]
public JsonObject? Meta { get; set; }
/// <summary>
/// Gets or sets the callable server tool corresponding to this metadata if any.
/// </summary>
[JsonIgnore]
public McpServerTool? McpServerTool { get; set; }
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DebuggerDisplay
{

View file

@ -182,7 +182,6 @@ internal sealed class AIFunctionMcpServerPrompt : McpServerPrompt
{
AIFunction = function;
ProtocolPrompt = prompt;
ProtocolPrompt.McpServerPrompt = this;
_metadata = metadata;
}

View file

@ -294,7 +294,6 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
{
AIFunction = function;
ProtocolResourceTemplate = resourceTemplate;
ProtocolResourceTemplate.McpServerResource = this;
ProtocolResource = resourceTemplate.AsResource();
_metadata = metadata;

View file

@ -234,7 +234,6 @@ internal sealed partial class AIFunctionMcpServerTool : McpServerTool
AIFunction = function;
ProtocolTool = tool;
ProtocolTool.McpServerTool = this;
_structuredOutputRequiresWrapping = structuredOutputRequiresWrapping;
_metadata = metadata;