Skip to content

Commit a2ee0c8

Browse files
Copilotstephentoub
andauthored
Address asymmetry in McpServerHandlers/McpClientHandlers and make all filter properties settable (#1337)
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>
1 parent 4f70954 commit a2ee0c8

File tree

6 files changed

+432
-293
lines changed

6 files changed

+432
-293
lines changed

src/ModelContextProtocol.Core/Client/McpClientHandlers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace ModelContextProtocol.Client;
2222
/// is done based on an ordinal, case-sensitive string comparison.
2323
/// </para>
2424
/// </remarks>
25-
public class McpClientHandlers
25+
public sealed class McpClientHandlers
2626
{
2727
/// <summary>Gets or sets notification handlers to register with the client.</summary>
2828
/// <remarks>

src/ModelContextProtocol.Core/Server/McpMessageFilters.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace ModelContextProtocol.Server;
88
public sealed class McpMessageFilters
99
{
1010
/// <summary>
11-
/// Gets the filters for all incoming JSON-RPC messages.
11+
/// Gets or sets the filters for all incoming JSON-RPC messages.
1212
/// </summary>
1313
/// <remarks>
1414
/// <para>
@@ -21,10 +21,18 @@ public sealed class McpMessageFilters
2121
/// the next handler in the pipeline, the default handlers will not be executed.
2222
/// </para>
2323
/// </remarks>
24-
public IList<McpMessageFilter> IncomingFilters { get; } = [];
24+
public IList<McpMessageFilter> IncomingFilters
25+
{
26+
get => field ??= [];
27+
set
28+
{
29+
Throw.IfNull(value);
30+
field = value;
31+
}
32+
}
2533

2634
/// <summary>
27-
/// Gets the filters for all outgoing JSON-RPC messages.
35+
/// Gets or sets the filters for all outgoing JSON-RPC messages.
2836
/// </summary>
2937
/// <remarks>
3038
/// <para>
@@ -38,5 +46,13 @@ public sealed class McpMessageFilters
3846
/// server-to-client messages.
3947
/// </para>
4048
/// </remarks>
41-
public IList<McpMessageFilter> OutgoingFilters { get; } = [];
49+
public IList<McpMessageFilter> OutgoingFilters
50+
{
51+
get => field ??= [];
52+
set
53+
{
54+
Throw.IfNull(value);
55+
field = value;
56+
}
57+
}
4258
}

src/ModelContextProtocol.Core/Server/McpRequestFilters.cs

Lines changed: 110 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace ModelContextProtocol.Server;
88
public sealed class McpRequestFilters
99
{
1010
/// <summary>
11-
/// Gets the filters for the list-tools handler pipeline.
11+
/// Gets or sets the filters for the list-tools handler pipeline.
1212
/// </summary>
1313
/// <remarks>
1414
/// <para>
@@ -22,20 +22,36 @@ public sealed class McpRequestFilters
2222
/// Tools from both sources will be combined when returning results to clients.
2323
/// </para>
2424
/// </remarks>
25-
public IList<McpRequestFilter<ListToolsRequestParams, ListToolsResult>> ListToolsFilters { get; } = [];
25+
public IList<McpRequestFilter<ListToolsRequestParams, ListToolsResult>> ListToolsFilters
26+
{
27+
get => field ??= [];
28+
set
29+
{
30+
Throw.IfNull(value);
31+
field = value;
32+
}
33+
}
2634

2735
/// <summary>
28-
/// Gets the filters for the call-tool handler pipeline.
36+
/// Gets or sets the filters for the call-tool handler pipeline.
2937
/// </summary>
3038
/// <remarks>
3139
/// 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.
3240
/// The filters can modify, log, or perform additional operations on requests and responses for
3341
/// <see cref="RequestMethods.ToolsCall"/> requests. The handler should implement logic to execute the requested tool and return appropriate results.
3442
/// </remarks>
35-
public IList<McpRequestFilter<CallToolRequestParams, CallToolResult>> CallToolFilters { get; } = [];
43+
public IList<McpRequestFilter<CallToolRequestParams, CallToolResult>> CallToolFilters
44+
{
45+
get => field ??= [];
46+
set
47+
{
48+
Throw.IfNull(value);
49+
field = value;
50+
}
51+
}
3652

3753
/// <summary>
38-
/// Gets the filters for the list-prompts handler pipeline.
54+
/// Gets or sets the filters for the list-prompts handler pipeline.
3955
/// </summary>
4056
/// <remarks>
4157
/// <para>
@@ -49,63 +65,111 @@ public sealed class McpRequestFilters
4965
/// Prompts from both sources will be combined when returning results to clients.
5066
/// </para>
5167
/// </remarks>
52-
public IList<McpRequestFilter<ListPromptsRequestParams, ListPromptsResult>> ListPromptsFilters { get; } = [];
68+
public IList<McpRequestFilter<ListPromptsRequestParams, ListPromptsResult>> ListPromptsFilters
69+
{
70+
get => field ??= [];
71+
set
72+
{
73+
Throw.IfNull(value);
74+
field = value;
75+
}
76+
}
5377

5478
/// <summary>
55-
/// Gets the filters for the get-prompt handler pipeline.
79+
/// Gets or sets the filters for the get-prompt handler pipeline.
5680
/// </summary>
5781
/// <remarks>
5882
/// 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.
5983
/// The filters can modify, log, or perform additional operations on requests and responses for
6084
/// <see cref="RequestMethods.PromptsGet"/> requests. The handler should implement logic to fetch or generate the requested prompt and return appropriate results.
6185
/// </remarks>
62-
public IList<McpRequestFilter<GetPromptRequestParams, GetPromptResult>> GetPromptFilters { get; } = [];
86+
public IList<McpRequestFilter<GetPromptRequestParams, GetPromptResult>> GetPromptFilters
87+
{
88+
get => field ??= [];
89+
set
90+
{
91+
Throw.IfNull(value);
92+
field = value;
93+
}
94+
}
6395

6496
/// <summary>
65-
/// Gets the filters for the list-resource-templates handler pipeline.
97+
/// Gets or sets the filters for the list-resource-templates handler pipeline.
6698
/// </summary>
6799
/// <remarks>
68100
/// These filters wrap handlers that return a list of available resource templates when requested by a client.
69101
/// The filters can modify, log, or perform additional operations on requests and responses for
70102
/// <see cref="RequestMethods.ResourcesTemplatesList"/> requests. It supports pagination through the cursor mechanism,
71103
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more resource templates.
72104
/// </remarks>
73-
public IList<McpRequestFilter<ListResourceTemplatesRequestParams, ListResourceTemplatesResult>> ListResourceTemplatesFilters { get; } = [];
105+
public IList<McpRequestFilter<ListResourceTemplatesRequestParams, ListResourceTemplatesResult>> ListResourceTemplatesFilters
106+
{
107+
get => field ??= [];
108+
set
109+
{
110+
Throw.IfNull(value);
111+
field = value;
112+
}
113+
}
74114

75115
/// <summary>
76-
/// Gets the filters for the list-resources handler pipeline.
116+
/// Gets or sets the filters for the list-resources handler pipeline.
77117
/// </summary>
78118
/// <remarks>
79119
/// These filters wrap handlers that return a list of available resources when requested by a client.
80120
/// The filters can modify, log, or perform additional operations on requests and responses for
81121
/// <see cref="RequestMethods.ResourcesList"/> requests. It supports pagination through the cursor mechanism,
82122
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more resources.
83123
/// </remarks>
84-
public IList<McpRequestFilter<ListResourcesRequestParams, ListResourcesResult>> ListResourcesFilters { get; } = [];
124+
public IList<McpRequestFilter<ListResourcesRequestParams, ListResourcesResult>> ListResourcesFilters
125+
{
126+
get => field ??= [];
127+
set
128+
{
129+
Throw.IfNull(value);
130+
field = value;
131+
}
132+
}
85133

86134
/// <summary>
87-
/// Gets the filters for the read-resource handler pipeline.
135+
/// Gets or sets the filters for the read-resource handler pipeline.
88136
/// </summary>
89137
/// <remarks>
90138
/// These filters wrap handlers that are invoked when a client requests the content of a specific resource identified by its URI.
91139
/// The filters can modify, log, or perform additional operations on requests and responses for
92140
/// <see cref="RequestMethods.ResourcesRead"/> requests. The handler should implement logic to locate and retrieve the requested resource.
93141
/// </remarks>
94-
public IList<McpRequestFilter<ReadResourceRequestParams, ReadResourceResult>> ReadResourceFilters { get; } = [];
142+
public IList<McpRequestFilter<ReadResourceRequestParams, ReadResourceResult>> ReadResourceFilters
143+
{
144+
get => field ??= [];
145+
set
146+
{
147+
Throw.IfNull(value);
148+
field = value;
149+
}
150+
}
95151

96152
/// <summary>
97-
/// Gets the filters for the complete-handler pipeline.
153+
/// Gets or sets the filters for the complete-handler pipeline.
98154
/// </summary>
99155
/// <remarks>
100156
/// These filters wrap handlers that provide auto-completion suggestions for prompt arguments or resource references in the Model Context Protocol.
101157
/// The filters can modify, log, or perform additional operations on requests and responses for
102158
/// <see cref="RequestMethods.CompletionComplete"/> requests. The handler processes auto-completion requests, returning a list of suggestions based on the
103159
/// reference type and current argument value.
104160
/// </remarks>
105-
public IList<McpRequestFilter<CompleteRequestParams, CompleteResult>> CompleteFilters { get; } = [];
161+
public IList<McpRequestFilter<CompleteRequestParams, CompleteResult>> CompleteFilters
162+
{
163+
get => field ??= [];
164+
set
165+
{
166+
Throw.IfNull(value);
167+
field = value;
168+
}
169+
}
106170

107171
/// <summary>
108-
/// Gets the filters for the subscribe-to-resources handler pipeline.
172+
/// Gets or sets the filters for the subscribe-to-resources handler pipeline.
109173
/// </summary>
110174
/// <remarks>
111175
/// <para>
@@ -119,10 +183,18 @@ public sealed class McpRequestFilters
119183
/// whenever a relevant resource is created, updated, or deleted.
120184
/// </para>
121185
/// </remarks>
122-
public IList<McpRequestFilter<SubscribeRequestParams, EmptyResult>> SubscribeToResourcesFilters { get; } = [];
186+
public IList<McpRequestFilter<SubscribeRequestParams, EmptyResult>> SubscribeToResourcesFilters
187+
{
188+
get => field ??= [];
189+
set
190+
{
191+
Throw.IfNull(value);
192+
field = value;
193+
}
194+
}
123195

124196
/// <summary>
125-
/// Gets the filters for the unsubscribe-from-resources handler pipeline.
197+
/// Gets or sets the filters for the unsubscribe-from-resources handler pipeline.
126198
/// </summary>
127199
/// <remarks>
128200
/// <para>
@@ -136,10 +208,18 @@ public sealed class McpRequestFilters
136208
/// to the client for the specified resources.
137209
/// </para>
138210
/// </remarks>
139-
public IList<McpRequestFilter<UnsubscribeRequestParams, EmptyResult>> UnsubscribeFromResourcesFilters { get; } = [];
211+
public IList<McpRequestFilter<UnsubscribeRequestParams, EmptyResult>> UnsubscribeFromResourcesFilters
212+
{
213+
get => field ??= [];
214+
set
215+
{
216+
Throw.IfNull(value);
217+
field = value;
218+
}
219+
}
140220

141221
/// <summary>
142-
/// Gets the filters for the set-logging-level handler pipeline.
222+
/// Gets or sets the filters for the set-logging-level handler pipeline.
143223
/// </summary>
144224
/// <remarks>
145225
/// <para>
@@ -153,5 +233,13 @@ public sealed class McpRequestFilters
153233
/// at or above the specified level to the client as notifications/message notifications.
154234
/// </para>
155235
/// </remarks>
156-
public IList<McpRequestFilter<SetLevelRequestParams, EmptyResult>> SetLoggingLevelFilters { get; } = [];
236+
public IList<McpRequestFilter<SetLevelRequestParams, EmptyResult>> SetLoggingLevelFilters
237+
{
238+
get => field ??= [];
239+
set
240+
{
241+
Throw.IfNull(value);
242+
field = value;
243+
}
244+
}
157245
}

src/ModelContextProtocol.Core/Server/McpServerFilters.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,28 @@ namespace ModelContextProtocol.Server;
1212
public sealed class McpServerFilters
1313
{
1414
/// <summary>
15-
/// Gets the filters for incoming and outgoing JSON-RPC messages.
15+
/// Gets or sets the filters for incoming and outgoing JSON-RPC messages.
1616
/// </summary>
17-
public McpMessageFilters Message { get; } = new();
17+
public McpMessageFilters Message
18+
{
19+
get => field ??= new();
20+
set
21+
{
22+
Throw.IfNull(value);
23+
field = value;
24+
}
25+
}
1826

1927
/// <summary>
20-
/// Gets the filters for request-specific MCP handler pipelines.
28+
/// Gets or sets the filters for request-specific MCP handler pipelines.
2129
/// </summary>
22-
public McpRequestFilters Request { get; } = new();
30+
public McpRequestFilters Request
31+
{
32+
get => field ??= new();
33+
set
34+
{
35+
Throw.IfNull(value);
36+
field = value;
37+
}
38+
}
2339
}

src/ModelContextProtocol.Core/Server/McpServerOptions.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,33 @@ public sealed class McpServerOptions
9797
/// <summary>
9898
/// Gets or sets the container of handlers used by the server for processing protocol messages.
9999
/// </summary>
100-
public McpServerHandlers Handlers { get; } = new();
100+
public McpServerHandlers Handlers
101+
{
102+
get => field ??= new();
103+
set
104+
{
105+
Throw.IfNull(value);
106+
field = value;
107+
}
108+
}
101109

102110
/// <summary>
103-
/// Gets the filter collections for MCP server handlers.
111+
/// Gets or sets the filter collections for MCP server handlers.
104112
/// </summary>
105113
/// <remarks>
106114
/// This property provides access to filter collections that can be used to modify the behavior
107115
/// of various MCP server handlers. The first filter added is the outermost (first to execute),
108116
/// and each subsequent filter wraps closer to the handler.
109117
/// </remarks>
110-
public McpServerFilters Filters { get; } = new();
118+
public McpServerFilters Filters
119+
{
120+
get => field ??= new();
121+
set
122+
{
123+
Throw.IfNull(value);
124+
field = value;
125+
}
126+
}
111127

112128
/// <summary>
113129
/// Gets or sets a collection of tools served by the server.

0 commit comments

Comments
 (0)