Split configuration of request and message filters (#1308)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Stephen Halter 2026-02-19 17:42:35 -08:00 • committed by GitHub
commit d21aa9e5a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 1288 additions and 1078 deletions

View file

@ -0,0 +1,42 @@
using ModelContextProtocol.Protocol;
namespace ModelContextProtocol.Server;
/// <summary>
/// Provides grouped message filter collections.
/// </summary>
public sealed class McpMessageFilters
{
/// <summary>
/// Gets the filters for all incoming JSON-RPC messages.
/// </summary>
/// <remarks>
/// <para>
/// These filters intercept all incoming JSON-RPC messages before they are processed by the server,
/// including requests, notifications, responses, and errors. The filters can perform logging,
/// authentication, rate limiting, or other cross-cutting concerns that apply to all message types.
/// </para>
/// <para>
/// Message filters are applied before request-specific filters. If a message filter does not call
/// the next handler in the pipeline, the default handlers will not be executed.
/// </para>
/// </remarks>
public IList<McpMessageFilter> IncomingFilters { get; } = [];
/// <summary>
/// Gets the filters for all outgoing JSON-RPC messages.
/// </summary>
/// <remarks>
/// <para>
/// These filters intercept all outgoing JSON-RPC messages before they are sent to the client,
/// including responses, notifications, and errors. The filters can perform logging,
/// redaction, auditing, or other cross-cutting concerns that apply to all message types.
/// </para>
/// <para>
/// If a message filter does not call the next handler in the pipeline, the message will not be sent.
/// Filters may also call the next handler multiple times with different messages to emit additional
/// server-to-client messages.
/// </para>
/// </remarks>
public IList<McpMessageFilter> OutgoingFilters { get; } = [];
}

View file

@ -0,0 +1,157 @@
using ModelContextProtocol.Protocol;
namespace ModelContextProtocol.Server;
/// <summary>
/// Provides grouped request-specific filter collections.
/// </summary>
public sealed class McpRequestFilters
{
/// <summary>
/// Gets the filters for the list-tools handler pipeline.
/// </summary>
/// <remarks>
/// <para>
/// These filters wrap handlers that return a list of available tools when requested by a client.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ToolsList"/> requests. It supports pagination through the cursor mechanism,
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more tools.
/// </para>
/// <para>
/// These filters work alongside any tools defined in the <see cref="McpServerTool"/> collection.
/// Tools from both sources will be combined when returning results to clients.
/// </para>
/// </remarks>
public IList<McpRequestFilter<ListToolsRequestParams, ListToolsResult>> ListToolsFilters { get; } = [];
/// <summary>
/// Gets the filters for the call-tool handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that are invoked when a client makes a call to a tool that isn't found in the <see cref="McpServerTool"/> collection.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ToolsCall"/> requests. The handler should implement logic to execute the requested tool and return appropriate results.
/// </remarks>
public IList<McpRequestFilter<CallToolRequestParams, CallToolResult>> CallToolFilters { get; } = [];
/// <summary>
/// Gets the filters for the list-prompts handler pipeline.
/// </summary>
/// <remarks>
/// <para>
/// These filters wrap handlers that return a list of available prompts when requested by a client.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.PromptsList"/> requests. It supports pagination through the cursor mechanism,
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more prompts.
/// </para>
/// <para>
/// These filters work alongside any prompts defined in the <see cref="McpServerPrompt"/> collection.
/// Prompts from both sources will be combined when returning results to clients.
/// </para>
/// </remarks>
public IList<McpRequestFilter<ListPromptsRequestParams, ListPromptsResult>> ListPromptsFilters { get; } = [];
/// <summary>
/// Gets the filters for the get-prompt handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that are invoked when a client requests details for a specific prompt that isn't found in the <see cref="McpServerPrompt"/> collection.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.PromptsGet"/> requests. The handler should implement logic to fetch or generate the requested prompt and return appropriate results.
/// </remarks>
public IList<McpRequestFilter<GetPromptRequestParams, GetPromptResult>> GetPromptFilters { get; } = [];
/// <summary>
/// Gets the filters for the list-resource-templates handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that return a list of available resource templates when requested by a client.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ResourcesTemplatesList"/> requests. It supports pagination through the cursor mechanism,
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more resource templates.
/// </remarks>
public IList<McpRequestFilter<ListResourceTemplatesRequestParams, ListResourceTemplatesResult>> ListResourceTemplatesFilters { get; } = [];
/// <summary>
/// Gets the filters for the list-resources handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that return a list of available resources when requested by a client.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ResourcesList"/> requests. It supports pagination through the cursor mechanism,
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more resources.
/// </remarks>
public IList<McpRequestFilter<ListResourcesRequestParams, ListResourcesResult>> ListResourcesFilters { get; } = [];
/// <summary>
/// Gets the filters for the read-resource handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that are invoked when a client requests the content of a specific resource identified by its URI.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ResourcesRead"/> requests. The handler should implement logic to locate and retrieve the requested resource.
/// </remarks>
public IList<McpRequestFilter<ReadResourceRequestParams, ReadResourceResult>> ReadResourceFilters { get; } = [];
/// <summary>
/// Gets the filters for the complete-handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that provide auto-completion suggestions for prompt arguments or resource references in the Model Context Protocol.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.CompletionComplete"/> requests. The handler processes auto-completion requests, returning a list of suggestions based on the
/// reference type and current argument value.
/// </remarks>
public IList<McpRequestFilter<CompleteRequestParams, CompleteResult>> CompleteFilters { get; } = [];
/// <summary>
/// Gets the filters for the subscribe-to-resources handler pipeline.
/// </summary>
/// <remarks>
/// <para>
/// These filters wrap handlers that are invoked when a client wants to receive notifications about changes to specific resources or resource patterns.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ResourcesSubscribe"/> requests. The handler should implement logic to register the client's interest in the specified resources
/// and set up the necessary infrastructure to send notifications when those resources change.
/// </para>
/// <para>
/// After a successful subscription, the server should send resource change notifications to the client
/// whenever a relevant resource is created, updated, or deleted.
/// </para>
/// </remarks>
public IList<McpRequestFilter<SubscribeRequestParams, EmptyResult>> SubscribeToResourcesFilters { get; } = [];
/// <summary>
/// Gets the filters for the unsubscribe-from-resources handler pipeline.
/// </summary>
/// <remarks>
/// <para>
/// These filters wrap handlers that are invoked when a client wants to stop receiving notifications about previously subscribed resources.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ResourcesUnsubscribe"/> requests. The handler should implement logic to remove the client's subscriptions to the specified resources
/// and clean up any associated resources.
/// </para>
/// <para>
/// After a successful unsubscription, the server should no longer send resource change notifications
/// to the client for the specified resources.
/// </para>
/// </remarks>
public IList<McpRequestFilter<UnsubscribeRequestParams, EmptyResult>> UnsubscribeFromResourcesFilters { get; } = [];
/// <summary>
/// Gets the filters for the set-logging-level handler pipeline.
/// </summary>
/// <remarks>
/// <para>
/// These filters wrap handlers that process <see cref="RequestMethods.LoggingSetLevel"/> requests from clients. When set, it enables
/// clients to control which log messages they receive by specifying a minimum severity threshold.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.LoggingSetLevel"/> requests.
/// </para>
/// <para>
/// After handling a level change request, the server typically begins sending log messages
/// at or above the specified level to the client as notifications/message notifications.
/// </para>
/// </remarks>
public IList<McpRequestFilter<SetLevelRequestParams, EmptyResult>> SetLoggingLevelFilters { get; } = [];
}

View file

@ -12,183 +12,12 @@ namespace ModelContextProtocol.Server;
public sealed class McpServerFilters
{
/// <summary>
/// Gets the filters for all incoming JSON-RPC messages.
/// Gets the filters for incoming and outgoing JSON-RPC messages.
/// </summary>
/// <remarks>
/// <para>
/// These filters intercept all incoming JSON-RPC messages before they are processed by the server,
/// including requests, notifications, responses, and errors. The filters can perform logging,
/// authentication, rate limiting, or other cross-cutting concerns that apply to all message types.
/// </para>
/// <para>
/// Message filters are applied before request-specific filters. If a message filter does not call
/// the next handler in the pipeline, the default handlers will not be executed.
/// </para>
/// </remarks>
public List<McpMessageFilter> IncomingMessageFilters { get; } = [];
public McpMessageFilters Message { get; } = new();
/// <summary>
/// Gets the filters for all outgoing JSON-RPC messages.
/// Gets the filters for request-specific MCP handler pipelines.
/// </summary>
/// <remarks>
/// <para>
/// These filters intercept all outgoing JSON-RPC messages before they are sent to the client,
/// including responses, notifications, and errors. The filters can perform logging,
/// redaction, auditing, or other cross-cutting concerns that apply to all message types.
/// </para>
/// <para>
/// If a message filter does not call the next handler in the pipeline, the message will not be sent.
/// Filters may also call the next handler multiple times with different messages to emit additional
/// server-to-client messages.
/// </para>
/// </remarks>
public List<McpMessageFilter> OutgoingMessageFilters { get; } = [];
/// <summary>
/// Gets the filters for the list-tools handler pipeline.
/// </summary>
/// <remarks>
/// <para>
/// These filters wrap handlers that return a list of available tools when requested by a client.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ToolsList"/> requests. It supports pagination through the cursor mechanism,
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more tools.
/// </para>
/// <para>
/// These filters work alongside any tools defined in the <see cref="McpServerTool"/> collection.
/// Tools from both sources will be combined when returning results to clients.
/// </para>
/// </remarks>
public List<McpRequestFilter<ListToolsRequestParams, ListToolsResult>> ListToolsFilters { get; } = [];
/// <summary>
/// Gets the filters for the call-tool handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that are invoked when a client makes a call to a tool that isn't found in the <see cref="McpServerTool"/> collection.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ToolsCall"/> requests. The handler should implement logic to execute the requested tool and return appropriate results.
/// </remarks>
public List<McpRequestFilter<CallToolRequestParams, CallToolResult>> CallToolFilters { get; } = [];
/// <summary>
/// Gets the filters for the list-prompts handler pipeline.
/// </summary>
/// <remarks>
/// <para>
/// These filters wrap handlers that return a list of available prompts when requested by a client.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.PromptsList"/> requests. It supports pagination through the cursor mechanism,
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more prompts.
/// </para>
/// <para>
/// These filters work alongside any prompts defined in the <see cref="McpServerPrompt"/> collection.
/// Prompts from both sources will be combined when returning results to clients.
/// </para>
/// </remarks>
public List<McpRequestFilter<ListPromptsRequestParams, ListPromptsResult>> ListPromptsFilters { get; } = [];
/// <summary>
/// Gets the filters for the get-prompt handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that are invoked when a client requests details for a specific prompt that isn't found in the <see cref="McpServerPrompt"/> collection.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.PromptsGet"/> requests. The handler should implement logic to fetch or generate the requested prompt and return appropriate results.
/// </remarks>
public List<McpRequestFilter<GetPromptRequestParams, GetPromptResult>> GetPromptFilters { get; } = [];
/// <summary>
/// Gets the filters for the list-resource-templates handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that return a list of available resource templates when requested by a client.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ResourcesTemplatesList"/> requests. It supports pagination through the cursor mechanism,
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more resource templates.
/// </remarks>
public List<McpRequestFilter<ListResourceTemplatesRequestParams, ListResourceTemplatesResult>> ListResourceTemplatesFilters { get; } = [];
/// <summary>
/// Gets the filters for the list-resources handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that return a list of available resources when requested by a client.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ResourcesList"/> requests. It supports pagination through the cursor mechanism,
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more resources.
/// </remarks>
public List<McpRequestFilter<ListResourcesRequestParams, ListResourcesResult>> ListResourcesFilters { get; } = [];
/// <summary>
/// Gets the filters for the read-resource handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that are invoked when a client requests the content of a specific resource identified by its URI.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ResourcesRead"/> requests. The handler should implement logic to locate and retrieve the requested resource.
/// </remarks>
public List<McpRequestFilter<ReadResourceRequestParams, ReadResourceResult>> ReadResourceFilters { get; } = [];
/// <summary>
/// Gets the filters for the complete-handler pipeline.
/// </summary>
/// <remarks>
/// These filters wrap handlers that provide auto-completion suggestions for prompt arguments or resource references in the Model Context Protocol.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.CompletionComplete"/> requests. The handler processes auto-completion requests, returning a list of suggestions based on the
/// reference type and current argument value.
/// </remarks>
public List<McpRequestFilter<CompleteRequestParams, CompleteResult>> CompleteFilters { get; } = [];
/// <summary>
/// Gets the filters for the subscribe-to-resources handler pipeline.
/// </summary>
/// <remarks>
/// <para>
/// These filters wrap handlers that are invoked when a client wants to receive notifications about changes to specific resources or resource patterns.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ResourcesSubscribe"/> requests. The handler should implement logic to register the client's interest in the specified resources
/// and set up the necessary infrastructure to send notifications when those resources change.
/// </para>
/// <para>
/// After a successful subscription, the server should send resource change notifications to the client
/// whenever a relevant resource is created, updated, or deleted.
/// </para>
/// </remarks>
public List<McpRequestFilter<SubscribeRequestParams, EmptyResult>> SubscribeToResourcesFilters { get; } = [];
/// <summary>
/// Gets the filters for the unsubscribe-from-resources handler pipeline.
/// </summary>
/// <remarks>
/// <para>
/// These filters wrap handlers that are invoked when a client wants to stop receiving notifications about previously subscribed resources.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.ResourcesUnsubscribe"/> requests. The handler should implement logic to remove the client's subscriptions to the specified resources
/// and clean up any associated resources.
/// </para>
/// <para>
/// After a successful unsubscription, the server should no longer send resource change notifications
/// to the client for the specified resources.
/// </para>
/// </remarks>
public List<McpRequestFilter<UnsubscribeRequestParams, EmptyResult>> UnsubscribeFromResourcesFilters { get; } = [];
/// <summary>
/// Gets the filters for the set-logging-level handler pipeline.
/// </summary>
/// <remarks>
/// <para>
/// These filters wrap handlers that process <see cref="RequestMethods.LoggingSetLevel"/> requests from clients. When set, it enables
/// clients to control which log messages they receive by specifying a minimum severity threshold.
/// The filters can modify, log, or perform additional operations on requests and responses for
/// <see cref="RequestMethods.LoggingSetLevel"/> requests.
/// </para>
/// <para>
/// After handling a level change request, the server typically begins sending log messages
/// at or above the specified level to the client as notifications/message notifications.
/// </para>
/// </remarks>
public List<McpRequestFilter<SetLevelRequestParams, EmptyResult>> SetLoggingLevelFilters { get; } = [];
public McpRequestFilters Request { get; } = new();
}

View file

@ -119,8 +119,8 @@ internal sealed partial class McpServerImpl : McpServer
}
// And initialize the session.
var incomingMessageFilter = BuildMessageFilterPipeline(options.Filters.IncomingMessageFilters);
var outgoingMessageFilter = BuildMessageFilterPipeline(options.Filters.OutgoingMessageFilters);
var incomingMessageFilter = BuildMessageFilterPipeline(options.Filters.Message.IncomingFilters);
var outgoingMessageFilter = BuildMessageFilterPipeline(options.Filters.Message.OutgoingFilters);
_sessionHandler = new McpSessionHandler(
isServer: true,
_sessionTransport,
@ -260,7 +260,7 @@ internal sealed partial class McpServerImpl : McpServer
}
completeHandler ??= (static async (_, __) => new CompleteResult());
completeHandler = BuildFilterPipeline(completeHandler, options.Filters.CompleteFilters);
completeHandler = BuildFilterPipeline(completeHandler, options.Filters.Request.CompleteFilters);
ServerCapabilities.Completions = new();
@ -366,9 +366,9 @@ internal sealed partial class McpServerImpl : McpServer
// subscribe = true;
}
listResourcesHandler = BuildFilterPipeline(listResourcesHandler, options.Filters.ListResourcesFilters);
listResourceTemplatesHandler = BuildFilterPipeline(listResourceTemplatesHandler, options.Filters.ListResourceTemplatesFilters);
readResourceHandler = BuildFilterPipeline(readResourceHandler, options.Filters.ReadResourceFilters, handler =>
listResourcesHandler = BuildFilterPipeline(listResourcesHandler, options.Filters.Request.ListResourcesFilters);
listResourceTemplatesHandler = BuildFilterPipeline(listResourceTemplatesHandler, options.Filters.Request.ListResourceTemplatesFilters);
readResourceHandler = BuildFilterPipeline(readResourceHandler, options.Filters.Request.ReadResourceFilters, handler =>
async (request, cancellationToken) =>
{
// Initial handler that sets MatchedPrimitive
@ -405,8 +405,8 @@ internal sealed partial class McpServerImpl : McpServer
throw;
}
});
subscribeHandler = BuildFilterPipeline(subscribeHandler, options.Filters.SubscribeToResourcesFilters);
unsubscribeHandler = BuildFilterPipeline(unsubscribeHandler, options.Filters.UnsubscribeFromResourcesFilters);
subscribeHandler = BuildFilterPipeline(subscribeHandler, options.Filters.Request.SubscribeToResourcesFilters);
unsubscribeHandler = BuildFilterPipeline(unsubscribeHandler, options.Filters.Request.UnsubscribeFromResourcesFilters);
ServerCapabilities.Resources.ListChanged = listChanged;
ServerCapabilities.Resources.Subscribe = subscribe;
@ -496,8 +496,8 @@ internal sealed partial class McpServerImpl : McpServer
listChanged = true;
}
listPromptsHandler = BuildFilterPipeline(listPromptsHandler, options.Filters.ListPromptsFilters);
getPromptHandler = BuildFilterPipeline(getPromptHandler, options.Filters.GetPromptFilters, handler =>
listPromptsHandler = BuildFilterPipeline(listPromptsHandler, options.Filters.Request.ListPromptsFilters);
getPromptHandler = BuildFilterPipeline(getPromptHandler, options.Filters.Request.GetPromptFilters, handler =>
async (request, cancellationToken) =>
{
// Initial handler that sets MatchedPrimitive
@ -618,8 +618,8 @@ internal sealed partial class McpServerImpl : McpServer
listChanged = true;
}
listToolsHandler = BuildFilterPipeline(listToolsHandler, options.Filters.ListToolsFilters);
callToolHandler = BuildFilterPipeline(callToolHandler, options.Filters.CallToolFilters, handler =>
listToolsHandler = BuildFilterPipeline(listToolsHandler, options.Filters.Request.ListToolsFilters);
callToolHandler = BuildFilterPipeline(callToolHandler, options.Filters.Request.CallToolFilters, handler =>
async (request, cancellationToken) =>
{
// Initial handler that sets MatchedPrimitive
@ -819,7 +819,7 @@ internal sealed partial class McpServerImpl : McpServer
// Apply filters to the handler
if (setLoggingLevelHandler is not null)
{
setLoggingLevelHandler = BuildFilterPipeline(setLoggingLevelHandler, options.Filters.SetLoggingLevelFilters);
setLoggingLevelHandler = BuildFilterPipeline(setLoggingLevelHandler, options.Filters.Request.SetLoggingLevelFilters);
}
ServerCapabilities.Logging = new();
@ -903,7 +903,7 @@ internal sealed partial class McpServerImpl : McpServer
private static McpRequestHandler<TParams, TResult> BuildFilterPipeline<TParams, TResult>(
McpRequestHandler<TParams, TResult> baseHandler,
List<McpRequestFilter<TParams, TResult>> filters,
IList<McpRequestFilter<TParams, TResult>> filters,
McpRequestFilter<TParams, TResult>? initialHandler = null)
{
var current = baseHandler;
@ -921,7 +921,7 @@ internal sealed partial class McpServerImpl : McpServer
return current;
}
private JsonRpcMessageFilter BuildMessageFilterPipeline(List<McpMessageFilter> filters)
private JsonRpcMessageFilter BuildMessageFilterPipeline(IList<McpMessageFilter> filters)
{
if (filters.Count == 0)
{

View file

@ -94,29 +94,21 @@ public sealed class McpServerOptions
/// </remarks>
public ClientCapabilities? KnownClientCapabilities { get; set; }
/// <summary>
/// Gets or sets the container of handlers used by the server for processing protocol messages.
/// </summary>
public McpServerHandlers Handlers { get; } = new();
/// <summary>
/// Gets the filter collections for MCP server handlers.
/// </summary>
/// <remarks>
/// This property provides access to filter collections that can be used to modify the behavior
/// of various MCP server handlers. Filters are applied in reverse order, so the last filter
/// added will be the outermost (first to execute).
/// of various MCP server handlers. The first filter added is the outermost (first to execute),
/// and each subsequent filter wraps closer to the handler.
/// </remarks>
public McpServerFilters Filters { get; } = new();
/// <summary>
/// Gets or sets the container of handlers used by the server for processing protocol messages.
/// </summary>
public McpServerHandlers Handlers
{
get => field ??= new();
set
{
Throw.IfNull(value);
field = value;
}
}
/// <summary>
/// Gets or sets a collection of tools served by the server.
/// </summary>

View file

@ -14,7 +14,8 @@ namespace ModelContextProtocol.Server;
/// </para>
/// <para>
/// This type is typically received as a parameter in message filter delegates registered via
/// <see cref="McpServerFilters.IncomingMessageFilters"/> or <see cref="McpServerFilters.OutgoingMessageFilters"/>.
/// <see cref="McpServerFilters.Message"/>'s <see cref="McpMessageFilters.IncomingFilters"/> or
/// <see cref="McpMessageFilters.OutgoingFilters"/> collections.
/// </para>
/// </remarks>
public class MessageContext