Skip to content

Commit c0e1d0d

Browse files
authored
Change handlers to support cancellation token (#99)
* wire the cancellation * missed file
1 parent 90dfc35 commit c0e1d0d

File tree

4 files changed

+39
-34
lines changed

4 files changed

+39
-34
lines changed

src/mcpdotnet/Client/McpClient.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
using System.Text.Json;
2-
using McpDotNet.Configuration;
1+
using McpDotNet.Configuration;
32
using McpDotNet.Logging;
43
using McpDotNet.Protocol.Messages;
54
using McpDotNet.Protocol.Transport;
65
using McpDotNet.Protocol.Types;
76
using McpDotNet.Shared;
7+
88
using Microsoft.Extensions.Logging;
99
using Microsoft.Extensions.Logging.Abstractions;
1010

11+
using System.Text.Json;
12+
1113
namespace McpDotNet.Client;
1214

1315
/// <inheritdoc/>
@@ -16,7 +18,7 @@ internal sealed class McpClient : McpJsonRpcEndpoint, IMcpClient
1618
private readonly McpClientOptions _options;
1719
private readonly ILogger _logger;
1820
private readonly IClientTransport _clientTransport;
19-
21+
2022
private volatile bool _isInitializing;
2123

2224
/// <summary>
@@ -44,7 +46,7 @@ public McpClient(IClientTransport transport, McpClientOptions options, McpServer
4446

4547
SetRequestHandler<CreateMessageRequestParams, CreateMessageResult>(
4648
"sampling/createMessage",
47-
request => samplingHandler(request, CancellationTokenSource?.Token ?? default));
49+
(request, ct) => samplingHandler(request, ct));
4850
}
4951

5052
if (options.Capabilities?.Roots is { } rootsCapability)
@@ -56,7 +58,7 @@ public McpClient(IClientTransport transport, McpClientOptions options, McpServer
5658

5759
SetRequestHandler<ListRootsRequestParams, ListRootsResult>(
5860
"roots/list",
59-
request => rootsHandler(request, CancellationTokenSource?.Token ?? default));
61+
(request, ct) => rootsHandler(request, ct));
6062
}
6163
}
6264

src/mcpdotnet/Configuration/McpServerBuilderExtensions.Tools.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
using System.Reflection;
2-
using System.Text.Json;
3-
using McpDotNet.Configuration;
1+
using McpDotNet.Configuration;
42
using McpDotNet.Protocol.Types;
53
using McpDotNet.Server;
64
using McpDotNet.Utils;
5+
76
using Microsoft.Extensions.AI;
87

8+
using System.Reflection;
9+
using System.Text.Json;
10+
911
namespace McpDotNet;
1012

1113
/// <summary>

src/mcpdotnet/Server/McpServer.cs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
using System.Text.Json.Nodes;
2-
using McpDotNet.Logging;
1+
using McpDotNet.Logging;
32
using McpDotNet.Protocol.Transport;
43
using McpDotNet.Protocol.Types;
54
using McpDotNet.Shared;
65
using McpDotNet.Utils;
6+
77
using Microsoft.Extensions.Logging;
88
using Microsoft.Extensions.Logging.Abstractions;
99

10+
using System.Text.Json.Nodes;
11+
1012
namespace McpDotNet.Server;
1113

1214
/// <inheritdoc />
@@ -107,14 +109,14 @@ public async Task StartAsync(CancellationToken cancellationToken = default)
107109

108110
private void SetPingHandler()
109111
{
110-
SetRequestHandler<JsonNode, PingResult>("ping",
111-
request => Task.FromResult(new PingResult()));
112+
SetRequestHandler<JsonNode, PingResult>("ping",
113+
(request, _) => Task.FromResult(new PingResult()));
112114
}
113115

114116
private void SetInitializeHandler(McpServerOptions options)
115117
{
116118
SetRequestHandler<InitializeRequestParams, InitializeResult>("initialize",
117-
request =>
119+
(request, _) =>
118120
{
119121
ClientCapabilities = request?.Capabilities ?? new();
120122
ClientInfo = request?.ClientInfo;
@@ -133,8 +135,8 @@ private void SetCompletionHandler(McpServerOptions options)
133135
// This capability is not optional, so return an empty result if there is no handler.
134136
SetRequestHandler<CompleteRequestParams, CompleteResult>("completion/complete",
135137
options.GetCompletionHandler is { } handler ?
136-
request => handler(new(this, request), CancellationTokenSource?.Token ?? default) :
137-
request => Task.FromResult(new CompleteResult() { Completion = new() { Values = [], Total = 0, HasMore = false } }));
138+
(request, ct) => handler(new(this, request), ct) :
139+
(request, ct) => Task.FromResult(new CompleteResult() { Completion = new() { Values = [], Total = 0, HasMore = false } }));
138140
}
139141

140142
private void SetResourcesHandler(McpServerOptions options)
@@ -150,9 +152,8 @@ private void SetResourcesHandler(McpServerOptions options)
150152
throw new McpServerException("Resources capability was enabled, but ListResources and/or ReadResource handlers were not specified.");
151153
}
152154

153-
CancellationToken cancellationToken = CancellationTokenSource?.Token ?? default;
154-
SetRequestHandler<ListResourcesRequestParams, ListResourcesResult>("resources/list", request => listResourcesHandler(new(this, request), cancellationToken));
155-
SetRequestHandler<ReadResourceRequestParams, ReadResourceResult>("resources/read", request => readResourceHandler(new(this, request), cancellationToken));
155+
SetRequestHandler<ListResourcesRequestParams, ListResourcesResult>("resources/list", (request, ct) => listResourcesHandler(new(this, request), ct));
156+
SetRequestHandler<ReadResourceRequestParams, ReadResourceResult>("resources/read", (request, ct) => readResourceHandler(new(this, request), ct));
156157

157158
if (resourcesCapability.Subscribe is not true)
158159
{
@@ -166,8 +167,8 @@ private void SetResourcesHandler(McpServerOptions options)
166167
throw new McpServerException("Resources capability was enabled with subscribe support, but SubscribeToResources and/or UnsubscribeFromResources handlers were not specified.");
167168
}
168169

169-
SetRequestHandler<SubscribeRequestParams, EmptyResult>("resources/subscribe", request => subscribeHandler(new(this, request), cancellationToken));
170-
SetRequestHandler<UnsubscribeRequestParams, EmptyResult>("resources/unsubscribe", request => unsubscribeHandler(new(this, request), cancellationToken));
170+
SetRequestHandler<SubscribeRequestParams, EmptyResult>("resources/subscribe", (request, ct) => subscribeHandler(new(this, request), ct));
171+
SetRequestHandler<UnsubscribeRequestParams, EmptyResult>("resources/unsubscribe", (request, ct) => unsubscribeHandler(new(this, request), ct));
171172
}
172173

173174
private void SetPromptsHandler(McpServerOptions options)
@@ -183,9 +184,8 @@ private void SetPromptsHandler(McpServerOptions options)
183184
throw new McpServerException("Prompts capability was enabled, but ListPrompts and/or GetPrompt handlers were not specified.");
184185
}
185186

186-
CancellationToken cancellationToken = CancellationTokenSource?.Token ?? default;
187-
SetRequestHandler<ListPromptsRequestParams, ListPromptsResult>("prompts/list", request => listPromptsHandler(new(this, request), cancellationToken));
188-
SetRequestHandler<GetPromptRequestParams, GetPromptResult>("prompts/get", request => getPromptHandler(new(this, request), cancellationToken));
187+
SetRequestHandler<ListPromptsRequestParams, ListPromptsResult>("prompts/list", (request, ct) => listPromptsHandler(new(this, request), ct));
188+
SetRequestHandler<GetPromptRequestParams, GetPromptResult>("prompts/get", (request, ct) => getPromptHandler(new(this, request), ct));
189189
}
190190

191191
private void SetToolsHandler(McpServerOptions options)
@@ -201,8 +201,7 @@ private void SetToolsHandler(McpServerOptions options)
201201
throw new McpServerException("ListTools and/or CallTool handlers were specified but the Tools capability was not enabled.");
202202
}
203203

204-
CancellationToken cancellationToken = CancellationTokenSource?.Token ?? default;
205-
SetRequestHandler<ListToolsRequestParams, ListToolsResult>("tools/list", request => listToolsHandler(new(this, request), cancellationToken));
206-
SetRequestHandler<CallToolRequestParams, CallToolResponse>("tools/call", request => callToolHandler(new(this, request), cancellationToken));
204+
SetRequestHandler<ListToolsRequestParams, ListToolsResult>("tools/list", (request, ct) => listToolsHandler(new(this, request), ct));
205+
SetRequestHandler<CallToolRequestParams, CallToolResponse>("tools/call", (request, ct) => callToolHandler(new(this, request), ct));
207206
}
208207
}

src/mcpdotnet/Shared/McpJsonRpcEndpoint.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
using System.Collections.Concurrent;
2-
using System.Text.Json;
3-
using McpDotNet.Client;
1+
using McpDotNet.Client;
42
using McpDotNet.Logging;
53
using McpDotNet.Protocol.Messages;
64
using McpDotNet.Protocol.Transport;
75
using McpDotNet.Utils;
86
using McpDotNet.Utils.Json;
7+
98
using Microsoft.Extensions.Logging;
109
using Microsoft.Extensions.Logging.Abstractions;
1110

11+
using System.Collections.Concurrent;
12+
using System.Text.Json;
13+
1214
namespace McpDotNet.Shared;
1315

1416
/// <summary>
@@ -23,7 +25,7 @@ internal abstract class McpJsonRpcEndpoint : IAsyncDisposable
2325
private readonly ITransport _transport;
2426
private readonly ConcurrentDictionary<RequestId, TaskCompletionSource<IJsonRpcMessage>> _pendingRequests;
2527
private readonly ConcurrentDictionary<string, List<Func<JsonRpcNotification, Task>>> _notificationHandlers;
26-
private readonly Dictionary<string, Func<JsonRpcRequest, Task<object?>>> _requestHandlers = [];
28+
private readonly Dictionary<string, Func<JsonRpcRequest, CancellationToken, Task<object?>>> _requestHandlers = [];
2729
private int _nextRequestId;
2830
private readonly JsonSerializerOptions _jsonOptions;
2931
private readonly ILogger _logger;
@@ -175,7 +177,7 @@ private async Task HandleRequest(JsonRpcRequest request, CancellationToken cance
175177
try
176178
{
177179
_logger.RequestHandlerCalled(EndpointName, request.Method);
178-
var result = await handler(request).ConfigureAwait(false);
180+
var result = await handler(request, cancellationToken).ConfigureAwait(false);
179181
_logger.RequestHandlerCompleted(EndpointName, request.Method);
180182
await _transport.SendMessageAsync(new JsonRpcResponse
181183
{
@@ -329,18 +331,18 @@ public async ValueTask DisposeAsync()
329331
/// <typeparam name="TResponse">Type of response payload (not full RPC response</typeparam>
330332
/// <param name="method">Method identifier to register for</param>
331333
/// <param name="handler">Handler to be called when a request with specified method identifier is received</param>
332-
protected void SetRequestHandler<TRequest, TResponse>(string method, Func<TRequest?, Task<TResponse>> handler)
334+
protected void SetRequestHandler<TRequest, TResponse>(string method, Func<TRequest?, CancellationToken, Task<TResponse>> handler)
333335
{
334336
Throw.IfNull(method);
335337
Throw.IfNull(handler);
336338

337-
_requestHandlers[method] = async (request) =>
339+
_requestHandlers[method] = async (request, cancellationToken) =>
338340
{
339341
// Convert the params JsonElement to our type using the same options
340342
var jsonString = JsonSerializer.Serialize(request.Params);
341343
var typedRequest = JsonSerializer.Deserialize<TRequest>(jsonString, _jsonOptions);
342344

343-
return await handler(typedRequest).ConfigureAwait(false);
345+
return await handler(typedRequest, cancellationToken).ConfigureAwait(false);
344346
};
345347
}
346348

0 commit comments

Comments
 (0)