Split configuration of request and message filters#1308
Split configuration of request and message filters#1308
Conversation
This adds WithMessageFilters and WithRequestFilters extension methods to IMcpServerBuilder which expose an IMcpMessageFilterBuilder and IMcpRequestFilterBuilder to their respective callbacks. This avoids having to many similar looking, poorly sorted extension methods on IMcpServerBuilder. This also keeps incoming and outgoing message filters more closely associated.
There was a problem hiding this comment.
Pull request overview
This PR refactors the filter configuration API for MCP servers by introducing a grouped builder pattern for configuring message and request filters. Instead of having many similarly-named Add*Filter extension methods directly on IMcpServerBuilder, the PR introduces two new methods (WithMessageFilters and WithRequestFilters) that accept callbacks providing specialized builder instances for configuring filters in their respective categories.
Changes:
- Introduces
IMcpMessageFilterBuilderandIMcpRequestFilterBuilderinterfaces with corresponding implementations for grouping filter configuration - Adds
WithMessageFilters()andWithRequestFilters()extension methods to organize filter registration - Restructures
McpServerFiltersto use nestedMcpMessageFiltersandMcpRequestFiltersobjects for better organization - Marks all existing individual filter methods as
[Obsolete]while maintaining backward compatibility through delegation - Updates all tests, samples, and documentation to use the new API pattern
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/ModelContextProtocol/McpServerBuilderExtensions.cs |
Adds new WithMessageFilters and WithRequestFilters methods; marks old methods obsolete |
src/ModelContextProtocol/McpMessageFilterBuilderExtensions.cs |
New file with AddIncomingFilter and AddOutgoingFilter methods |
src/ModelContextProtocol/McpRequestFilterBuilderExtensions.cs |
New file with all request-specific filter methods (AddListToolsFilter, AddCallToolFilter, etc.) |
src/ModelContextProtocol/IMcpMessageFilterBuilder.cs |
New interface for message filter builder |
src/ModelContextProtocol/IMcpRequestFilterBuilder.cs |
New interface for request filter builder |
src/ModelContextProtocol/DefaultMcpMessageFilterBuilder.cs |
Default implementation of message filter builder |
src/ModelContextProtocol/DefaultMcpRequestFilterBuilder.cs |
Default implementation of request filter builder |
src/ModelContextProtocol.Core/Server/McpServerFilters.cs |
Restructured to use Message and Request nested properties |
src/ModelContextProtocol.Core/Server/McpMessageFilters.cs |
New class containing message filter collections |
src/ModelContextProtocol.Core/Server/McpRequestFilters.cs |
New class containing request-specific filter collections |
src/ModelContextProtocol.Core/Server/McpServerImpl.cs |
Updated to access filters via nested properties |
src/ModelContextProtocol.Core/Server/MessageContext.cs |
Updated XML documentation to reference new nested structure |
src/ModelContextProtocol.Core/Server/McpServerOptions.cs |
Updated filter execution order documentation for clarity |
src/ModelContextProtocol.AspNetCore/AuthorizationFilterSetup.cs |
Updated to use new nested filter properties |
tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsMessageFilterTests.cs |
All tests updated to use new WithMessageFilters and WithRequestFilters APIs |
tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsFilterTests.cs |
All tests updated to use new WithRequestFilters API |
tests/ModelContextProtocol.ConformanceServer/Program.cs |
Updated to use new WithRequestFilters API |
docs/concepts/filters.md |
Comprehensive documentation updates showing new API usage patterns |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
| private const string MessageFilterObsoleteMessage = "Use WithMessageFilters() instead."; | ||
| private const string RequestFilterObsoleteMessage = "Use WithRequestFilters() instead."; |
There was a problem hiding this comment.
These should be defined in src/Common/Obsoletions.cs please, with:
public const string MessageAndRequestFilter_DiagnosticId = "MCP9002";
public const string MessageAndRequestFilter_Url = "https://github.com/modelcontextprotocol/csharp-sdk/pull/1308";
public const string MessageFilter_Message = "Use WithMessageFilters() instead.";
public const string RequestFilter_Message = "Use WithRequestFilters() instead.";There was a problem hiding this comment.
@copilot Please use Oboletions.cs with a URL and Diagnotic ID as suggested.
mikekistler
left a comment
There was a problem hiding this comment.
I'm struggling a bit with this. Maybe its because I haven't had time to dig into message/request filters yet, but I think I would have trouble figuring out how to set these up and use them.
docs/concepts/filters.md
Outdated
| 1. **Message Filters** - Low-level filters (`AddIncomingFilter`, `AddOutgoingFilter`) configured via `WithMessageFilters(...)`. | ||
| 2. **Request-Specific Filters** - Handler-level filters (e.g., `AddListToolsFilter`, `AddCallToolFilter`) configured via `WithRequestFilters(...)`. |
There was a problem hiding this comment.
I think it's really important to describe what these filters do and not just how they are added.
| 2. **Request-Specific Filters** - Handler-level filters (e.g., `AddListToolsFilter`, `AddCallToolFilter`) configured via `WithRequestFilters(...)`. | ||
|
|
||
| The filters are stored in `McpServerOptions.Filters` and applied during server configuration. | ||
|
|
There was a problem hiding this comment.
I think we need a diagram that illustrates the flow on incoming and outgoing messages through these filters and the associated handlers.
There was a problem hiding this comment.
There is the following bit in the "Message Filter Execution Order", but it's not part of the diff.
Request arrives
↓
IncomingFilter1 (before next)
↓
IncomingFilter2 (before next)
↓
Request Routing → ListToolsFilter → Handler
↓
IncomingFilter2 (after next)
↓
IncomingFilter1 (after next)
↓
Response sent via OutgoingFilter1 (before next)
↓
OutgoingFilter2 (before next)
↓
Transport sends message
↓
OutgoingFilter2 (after next)
↓
OutgoingFilter1 (after next)
- We can add internal properties later to lazily check if the getters have been used to avoid allocations if we care
This adds
WithMessageFiltersandWithRequestFiltersextension methods toIMcpServerBuilderwhich expose anIMcpMessageFilterBuilderandIMcpRequestFilterBuilderinline to their respective callbacks.This avoids having too many similar looking, poorly sorted extension methods on
IMcpServerBuilder. This also keeps incoming and outgoing message filters more closely associated.