Overhaul McpClientFactory/McpServerFactory (#87)

Handlers are now specified before connecting the client/server. Otherwise, race conditions exist. Handlers move into being a part of the McpClientOptions/McpServerOptions, so that they're provided to the factories. Where relevant, the handlers are specified as part of the capability descriptors, so that they go hand in hand.

The factories are no longer stateful. Instead of allocating a factory and then calling a create method on it, you just call a static factory method.
This commit is contained in:
Stephen Toub 2025-03-16 12:08:52 -04:00 • committed by GitHub
commit 08b0a04dab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,5 +1,6 @@
using McpDotNet.Protocol.Messages;
using McpDotNet.Protocol.Types;
using McpDotNet.Utils;
namespace McpDotNet.Server;
@ -12,10 +13,7 @@ public static class McpServerExtensions
public static Task<CreateMessageResult> RequestSamplingAsync(
this IMcpServer server, CreateMessageRequestParams request, CancellationToken cancellationToken)
{
if (server is null)
{
throw new ArgumentNullException(nameof(server));
}
Throw.IfNull(server);
if (server.ClientCapabilities?.Sampling is null)
{
@ -33,10 +31,7 @@ public static class McpServerExtensions
public static Task<ListRootsResult> RequestRootsAsync(
this IMcpServer server, ListRootsRequestParams request, CancellationToken cancellationToken)
{
if (server is null)
{
throw new ArgumentNullException(nameof(server));
}
Throw.IfNull(server);
if (server.ClientCapabilities?.Roots is null)
{
@ -47,175 +42,4 @@ public static class McpServerExtensions
new JsonRpcRequest { Method = "roots/list", Params = request },
cancellationToken);
}
/// <summary>
/// Sets the handler for list tools requests.
/// </summary>
public static void SetListToolsHandler(
this IMcpServer server, Func<RequestContext<ListToolsRequestParams>, CancellationToken, Task<ListToolsResult>> handler)
{
if (server is null)
{
throw new ArgumentNullException(nameof(server));
}
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
server.SetOperationHandler(OperationNames.ListTools, handler);
}
/// <summary>
/// Sets the handler for call tool requests.
/// </summary>
public static void SetCallToolHandler(
this IMcpServer server, Func<RequestContext<CallToolRequestParams>, CancellationToken, Task<CallToolResponse>> handler)
{
if (server is null)
{
throw new ArgumentNullException(nameof(server));
}
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
server.SetOperationHandler(OperationNames.CallTool, handler);
}
/// <summary>
/// Sets the handler for list prompts requests.
/// </summary>
public static void SetListPromptsHandler(
this IMcpServer server, Func<RequestContext<ListPromptsRequestParams>, CancellationToken, Task<ListPromptsResult>> handler)
{
if (server is null)
{
throw new ArgumentNullException(nameof(server));
}
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
server.SetOperationHandler(OperationNames.ListPrompts, handler);
}
/// <summary>
/// Sets the handler for get prompt requests.
/// </summary>
public static void SetGetPromptHandler(
this IMcpServer server, Func<RequestContext<GetPromptRequestParams>, CancellationToken, Task<GetPromptResult>> handler)
{
if (server is null)
{
throw new ArgumentNullException(nameof(server));
}
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
server.SetOperationHandler(OperationNames.GetPrompt, handler);
}
/// <summary>
/// Sets the handler for list resources requests.
/// </summary>
public static void SetListResourcesHandler(
this IMcpServer server, Func<RequestContext<ListResourcesRequestParams>, CancellationToken, Task<ListResourcesResult>> handler)
{
if (server is null)
{
throw new ArgumentNullException(nameof(server));
}
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
server.SetOperationHandler(OperationNames.ListResources, handler);
}
/// <summary>
/// Sets the handler for read resource requests.
/// </summary>
public static void SetReadResourceHandler(
this IMcpServer server, Func<RequestContext<ReadResourceRequestParams>, CancellationToken, Task<ReadResourceResult>> handler)
{
if (server is null)
{
throw new ArgumentNullException(nameof(server));
}
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
server.SetOperationHandler(OperationNames.ReadResource, handler);
}
/// <summary>
/// Sets the handler for get completion requests.
/// </summary>
public static void SetGetCompletionHandler(
this IMcpServer server, Func<RequestContext<CompleteRequestParams>, CancellationToken, Task<CompleteResult>> handler)
{
if (server is null)
{
throw new ArgumentNullException(nameof(server));
}
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
server.SetOperationHandler(OperationNames.GetCompletion, handler);
}
/// <summary>
/// Sets the handler for subscribe to resources requests.
/// </summary>
public static void SetSubscribeToResourcesHandler(
this IMcpServer server, Func<RequestContext<string>, CancellationToken, Task> handler)
{
if (server is null)
{
throw new ArgumentNullException(nameof(server));
}
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
server.SetOperationHandler(OperationNames.SubscribeToResources, handler);
}
/// <summary>
/// Sets the handler for unsubscribe from resources requests.
/// </summary>
public static void SetUnsubscribeFromResourcesHandler(
this IMcpServer server, Func<RequestContext<string>, CancellationToken, Task> handler)
{
if (server is null)
{
throw new ArgumentNullException(nameof(server));
}
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
server.SetOperationHandler(OperationNames.UnsubscribeFromResources, handler);
}
}