diff --git a/src/mcpdotnet/Client/McpClient.cs b/src/mcpdotnet/Client/McpClient.cs index 4195f5d..c806d91 100644 --- a/src/mcpdotnet/Client/McpClient.cs +++ b/src/mcpdotnet/Client/McpClient.cs @@ -1,13 +1,15 @@ -using System.Text.Json; -using McpDotNet.Configuration; +using McpDotNet.Configuration; using McpDotNet.Logging; using McpDotNet.Protocol.Messages; using McpDotNet.Protocol.Transport; using McpDotNet.Protocol.Types; using McpDotNet.Shared; + using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using System.Text.Json; + namespace McpDotNet.Client; /// @@ -16,7 +18,7 @@ internal sealed class McpClient : McpJsonRpcEndpoint, IMcpClient private readonly McpClientOptions _options; private readonly ILogger _logger; private readonly IClientTransport _clientTransport; - + private volatile bool _isInitializing; /// @@ -44,7 +46,7 @@ internal sealed class McpClient : McpJsonRpcEndpoint, IMcpClient SetRequestHandler( "sampling/createMessage", - request => samplingHandler(request, CancellationTokenSource?.Token ?? default)); + (request, ct) => samplingHandler(request, ct)); } if (options.Capabilities?.Roots is { } rootsCapability) @@ -56,7 +58,7 @@ internal sealed class McpClient : McpJsonRpcEndpoint, IMcpClient SetRequestHandler( "roots/list", - request => rootsHandler(request, CancellationTokenSource?.Token ?? default)); + (request, ct) => rootsHandler(request, ct)); } } diff --git a/src/mcpdotnet/Configuration/McpServerBuilderExtensions.Tools.cs b/src/mcpdotnet/Configuration/McpServerBuilderExtensions.Tools.cs index c12f0ed..872c550 100644 --- a/src/mcpdotnet/Configuration/McpServerBuilderExtensions.Tools.cs +++ b/src/mcpdotnet/Configuration/McpServerBuilderExtensions.Tools.cs @@ -1,11 +1,13 @@ -using System.Reflection; -using System.Text.Json; -using McpDotNet.Configuration; +using McpDotNet.Configuration; using McpDotNet.Protocol.Types; using McpDotNet.Server; using McpDotNet.Utils; + using Microsoft.Extensions.AI; +using System.Reflection; +using System.Text.Json; + namespace McpDotNet; /// diff --git a/src/mcpdotnet/Server/McpServer.cs b/src/mcpdotnet/Server/McpServer.cs index b1308bb..764e24e 100644 --- a/src/mcpdotnet/Server/McpServer.cs +++ b/src/mcpdotnet/Server/McpServer.cs @@ -1,12 +1,14 @@ -using System.Text.Json.Nodes; -using McpDotNet.Logging; +using McpDotNet.Logging; using McpDotNet.Protocol.Transport; using McpDotNet.Protocol.Types; using McpDotNet.Shared; using McpDotNet.Utils; + using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using System.Text.Json.Nodes; + namespace McpDotNet.Server; /// @@ -107,14 +109,14 @@ internal sealed class McpServer : McpJsonRpcEndpoint, IMcpServer private void SetPingHandler() { - SetRequestHandler("ping", - request => Task.FromResult(new PingResult())); + SetRequestHandler("ping", + (request, _) => Task.FromResult(new PingResult())); } private void SetInitializeHandler(McpServerOptions options) { SetRequestHandler("initialize", - request => + (request, _) => { ClientCapabilities = request?.Capabilities ?? new(); ClientInfo = request?.ClientInfo; @@ -133,8 +135,8 @@ internal sealed class McpServer : McpJsonRpcEndpoint, IMcpServer // This capability is not optional, so return an empty result if there is no handler. SetRequestHandler("completion/complete", options.GetCompletionHandler is { } handler ? - request => handler(new(this, request), CancellationTokenSource?.Token ?? default) : - request => Task.FromResult(new CompleteResult() { Completion = new() { Values = [], Total = 0, HasMore = false } })); + (request, ct) => handler(new(this, request), ct) : + (request, ct) => Task.FromResult(new CompleteResult() { Completion = new() { Values = [], Total = 0, HasMore = false } })); } private void SetResourcesHandler(McpServerOptions options) @@ -150,9 +152,8 @@ internal sealed class McpServer : McpJsonRpcEndpoint, IMcpServer throw new McpServerException("Resources capability was enabled, but ListResources and/or ReadResource handlers were not specified."); } - CancellationToken cancellationToken = CancellationTokenSource?.Token ?? default; - SetRequestHandler("resources/list", request => listResourcesHandler(new(this, request), cancellationToken)); - SetRequestHandler("resources/read", request => readResourceHandler(new(this, request), cancellationToken)); + SetRequestHandler("resources/list", (request, ct) => listResourcesHandler(new(this, request), ct)); + SetRequestHandler("resources/read", (request, ct) => readResourceHandler(new(this, request), ct)); if (resourcesCapability.Subscribe is not true) { @@ -166,8 +167,8 @@ internal sealed class McpServer : McpJsonRpcEndpoint, IMcpServer throw new McpServerException("Resources capability was enabled with subscribe support, but SubscribeToResources and/or UnsubscribeFromResources handlers were not specified."); } - SetRequestHandler("resources/subscribe", request => subscribeHandler(new(this, request), cancellationToken)); - SetRequestHandler("resources/unsubscribe", request => unsubscribeHandler(new(this, request), cancellationToken)); + SetRequestHandler("resources/subscribe", (request, ct) => subscribeHandler(new(this, request), ct)); + SetRequestHandler("resources/unsubscribe", (request, ct) => unsubscribeHandler(new(this, request), ct)); } private void SetPromptsHandler(McpServerOptions options) @@ -183,9 +184,8 @@ internal sealed class McpServer : McpJsonRpcEndpoint, IMcpServer throw new McpServerException("Prompts capability was enabled, but ListPrompts and/or GetPrompt handlers were not specified."); } - CancellationToken cancellationToken = CancellationTokenSource?.Token ?? default; - SetRequestHandler("prompts/list", request => listPromptsHandler(new(this, request), cancellationToken)); - SetRequestHandler("prompts/get", request => getPromptHandler(new(this, request), cancellationToken)); + SetRequestHandler("prompts/list", (request, ct) => listPromptsHandler(new(this, request), ct)); + SetRequestHandler("prompts/get", (request, ct) => getPromptHandler(new(this, request), ct)); } private void SetToolsHandler(McpServerOptions options) @@ -201,8 +201,7 @@ internal sealed class McpServer : McpJsonRpcEndpoint, IMcpServer throw new McpServerException("ListTools and/or CallTool handlers were specified but the Tools capability was not enabled."); } - CancellationToken cancellationToken = CancellationTokenSource?.Token ?? default; - SetRequestHandler("tools/list", request => listToolsHandler(new(this, request), cancellationToken)); - SetRequestHandler("tools/call", request => callToolHandler(new(this, request), cancellationToken)); + SetRequestHandler("tools/list", (request, ct) => listToolsHandler(new(this, request), ct)); + SetRequestHandler("tools/call", (request, ct) => callToolHandler(new(this, request), ct)); } } diff --git a/src/mcpdotnet/Shared/McpJsonRpcEndpoint.cs b/src/mcpdotnet/Shared/McpJsonRpcEndpoint.cs index 880668b..cd7647f 100644 --- a/src/mcpdotnet/Shared/McpJsonRpcEndpoint.cs +++ b/src/mcpdotnet/Shared/McpJsonRpcEndpoint.cs @@ -1,14 +1,16 @@ -using System.Collections.Concurrent; -using System.Text.Json; -using McpDotNet.Client; +using McpDotNet.Client; using McpDotNet.Logging; using McpDotNet.Protocol.Messages; using McpDotNet.Protocol.Transport; using McpDotNet.Utils; using McpDotNet.Utils.Json; + using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using System.Collections.Concurrent; +using System.Text.Json; + namespace McpDotNet.Shared; /// @@ -23,7 +25,7 @@ internal abstract class McpJsonRpcEndpoint : IAsyncDisposable private readonly ITransport _transport; private readonly ConcurrentDictionary> _pendingRequests; private readonly ConcurrentDictionary>> _notificationHandlers; - private readonly Dictionary>> _requestHandlers = []; + private readonly Dictionary>> _requestHandlers = []; private int _nextRequestId; private readonly JsonSerializerOptions _jsonOptions; private readonly ILogger _logger; @@ -175,7 +177,7 @@ internal abstract class McpJsonRpcEndpoint : IAsyncDisposable try { _logger.RequestHandlerCalled(EndpointName, request.Method); - var result = await handler(request).ConfigureAwait(false); + var result = await handler(request, cancellationToken).ConfigureAwait(false); _logger.RequestHandlerCompleted(EndpointName, request.Method); await _transport.SendMessageAsync(new JsonRpcResponse { @@ -329,18 +331,18 @@ internal abstract class McpJsonRpcEndpoint : IAsyncDisposable /// Type of response payload (not full RPC response /// Method identifier to register for /// Handler to be called when a request with specified method identifier is received - protected void SetRequestHandler(string method, Func> handler) + protected void SetRequestHandler(string method, Func> handler) { Throw.IfNull(method); Throw.IfNull(handler); - _requestHandlers[method] = async (request) => + _requestHandlers[method] = async (request, cancellationToken) => { // Convert the params JsonElement to our type using the same options var jsonString = JsonSerializer.Serialize(request.Params); var typedRequest = JsonSerializer.Deserialize(jsonString, _jsonOptions); - return await handler(typedRequest).ConfigureAwait(false); + return await handler(typedRequest, cancellationToken).ConfigureAwait(false); }; }