Skip to content

Commit 180dcd0

Browse files
committed
Add Handler setters to support object expressions
1 parent 5a05a2b commit 180dcd0

File tree

11 files changed

+473
-408
lines changed

11 files changed

+473
-408
lines changed

README.md

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -178,49 +178,50 @@ McpServerOptions options = new()
178178
{
179179
Tools = new ToolsCapability(),
180180
},
181-
};
182-
183-
options.Handlers.ListToolsHandler = (request, cancellationToken) =>
184-
ValueTask.FromResult(new ListToolsResult
181+
Handlers = new McpServerHandlers()
185182
{
186-
Tools =
187-
[
188-
new Tool
183+
ListToolsHandler = (request, cancellationToken) =>
184+
ValueTask.FromResult(new ListToolsResult
185+
{
186+
Tools =
187+
[
188+
new Tool
189+
{
190+
Name = "echo",
191+
Description = "Echoes the input back to the client.",
192+
InputSchema = JsonSerializer.Deserialize<JsonElement>("""
193+
{
194+
"type": "object",
195+
"properties": {
196+
"message": {
197+
"type": "string",
198+
"description": "The input to echo back"
199+
}
200+
},
201+
"required": ["message"]
202+
}
203+
"""),
204+
}
205+
]
206+
}),
207+
CallToolHandler = (request, cancellationToken) =>
208+
{
209+
if (request.Params?.Name == "echo")
189210
{
190-
Name = "echo",
191-
Description = "Echoes the input back to the client.",
192-
InputSchema = JsonSerializer.Deserialize<JsonElement>("""
193-
{
194-
"type": "object",
195-
"properties": {
196-
"message": {
197-
"type": "string",
198-
"description": "The input to echo back"
199-
}
200-
},
201-
"required": ["message"]
202-
}
203-
"""),
211+
if (request.Params.Arguments?.TryGetValue("message", out var message) is not true)
212+
{
213+
throw new McpException("Missing required argument 'message'");
214+
}
215+
216+
return ValueTask.FromResult(new CallToolResult
217+
{
218+
Content = [new TextContentBlock { Text = $"Echo: {message}", Type = "text" }]
219+
});
204220
}
205-
]
206-
});
207221

208-
options.Handlers.CallToolHandler = (request, cancellationToken) =>
209-
{
210-
if (request.Params?.Name == "echo")
211-
{
212-
if (request.Params.Arguments?.TryGetValue("message", out var message) is not true)
213-
{
214-
throw new McpException("Missing required argument 'message'");
222+
throw new McpException($"Unknown tool: '{request.Params?.Name}'");
215223
}
216-
217-
return ValueTask.FromResult(new CallToolResult
218-
{
219-
Content = [new TextContentBlock { Text = $"Echo: {message}", Type = "text" }]
220-
});
221224
}
222-
223-
throw new McpException($"Unknown tool: '{request.Params?.Name}'");
224225
};
225226

226227
await using IMcpServer server = McpServerFactory.Create(new StdioServerTransport("MyServer"), options);

docs/concepts/elicitation/samples/client/Program.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717
{
1818
Name = "ElicitationClient",
1919
Version = "1.0.0"
20+
},
21+
Handlers = new()
22+
{
23+
ElicitationHandler = HandleElicitationAsync
2024
}
2125
};
22-
options.Handlers.ElicitationHandler = HandleElicitationAsync;
2326

2427
await using var mcpClient = await McpClientFactory.CreateAsync(clientTransport, options);
2528
// </snippet_McpInitialize>

samples/ChatWithTools/Program.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,20 @@
3232
.UseOpenTelemetry(loggerFactory: loggerFactory, configure: o => o.EnableSensitiveData = true)
3333
.Build();
3434

35-
var clientOptions = new McpClientOptions();
36-
clientOptions.Handlers.SamplingHandler = samplingClient.CreateSamplingHandler();
37-
3835
var mcpClient = await McpClientFactory.CreateAsync(
3936
new StdioClientTransport(new()
4037
{
4138
Command = "npx",
4239
Arguments = ["-y", "--verbose", "@modelcontextprotocol/server-everything"],
4340
Name = "Everything",
4441
}),
45-
clientOptions: clientOptions,
42+
clientOptions: new()
43+
{
44+
Handlers = new()
45+
{
46+
SamplingHandler = samplingClient.CreateSamplingHandler()
47+
}
48+
},
4649
loggerFactory: loggerFactory);
4750

4851
// Get all available tools

src/ModelContextProtocol.Core/Client/McpClientOptions.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ namespace ModelContextProtocol.Client;
1111
/// </remarks>
1212
public sealed class McpClientOptions
1313
{
14+
private McpClientHandlers? _handlers;
15+
1416
/// <summary>
1517
/// Gets or sets information about this client implementation, including its name and version.
1618
/// </summary>
@@ -67,5 +69,13 @@ public sealed class McpClientOptions
6769
/// <summary>
6870
/// Gets or sets the container of handlers used by the client for processing protocol messages.
6971
/// </summary>
70-
public McpClientHandlers Handlers { get; } = new();
72+
public McpClientHandlers Handlers
73+
{
74+
get => _handlers ??= new();
75+
set
76+
{
77+
Throw.IfNull(value);
78+
_handlers = value;
79+
}
80+
}
7181
}

src/ModelContextProtocol.Core/Server/McpServerOptions.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ namespace ModelContextProtocol.Server;
77
/// </summary>
88
public sealed class McpServerOptions
99
{
10+
private McpServerHandlers? _handlers;
11+
1012
/// <summary>
1113
/// Gets or sets information about this server implementation, including its name and version.
1214
/// </summary>
@@ -93,7 +95,15 @@ public sealed class McpServerOptions
9395
/// <summary>
9496
/// Gets or sets the container of handlers used by the server for processing protocol messages.
9597
/// </summary>
96-
public McpServerHandlers Handlers { get; } = new();
98+
public McpServerHandlers Handlers
99+
{
100+
get => _handlers ??= new();
101+
set
102+
{
103+
Throw.IfNull(value);
104+
_handlers = value;
105+
}
106+
}
97107

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

tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -161,21 +161,26 @@ public async Task Sampling_DoesNotCloseStream_Prematurely()
161161
await app.StartAsync(TestContext.Current.CancellationToken);
162162

163163
var sampleCount = 0;
164-
var clientOptions = new McpClientOptions();
165-
clientOptions.Handlers.SamplingHandler = async (parameters, _, _) =>
164+
var clientOptions = new McpClientOptions()
166165
{
167-
Assert.NotNull(parameters?.Messages);
168-
var message = Assert.Single(parameters.Messages);
169-
Assert.Equal(Role.User, message.Role);
170-
Assert.Equal("Test prompt for sampling", Assert.IsType<TextContentBlock>(message.Content).Text);
171-
172-
sampleCount++;
173-
return new CreateMessageResult
166+
Handlers = new()
174167
{
175-
Model = "test-model",
176-
Role = Role.Assistant,
177-
Content = new TextContentBlock { Text = "Sampling response from client" },
178-
};
168+
SamplingHandler = async (parameters, _, _) =>
169+
{
170+
Assert.NotNull(parameters?.Messages);
171+
var message = Assert.Single(parameters.Messages);
172+
Assert.Equal(Role.User, message.Role);
173+
Assert.Equal("Test prompt for sampling", Assert.IsType<TextContentBlock>(message.Content).Text);
174+
175+
sampleCount++;
176+
return new CreateMessageResult
177+
{
178+
Model = "test-model",
179+
Role = Role.Assistant,
180+
Content = new TextContentBlock { Text = "Sampling response from client" },
181+
};
182+
}
183+
}
179184
};
180185

181186
await using var mcpClient = await ConnectAsync(clientOptions: clientOptions);

0 commit comments

Comments
 (0)