Decouple Handlers and Collections from protocol types (#765)
* Decouple Handlers and Collections (e.g. ToolsHandler and ToolsCollection) from Capabilities by promoting them to the options types. Also, group all handlers in container classes, for server, I decided to move McpServerHandlers down to Mcp.Core and move the NotificationHandlers there too. Also, Fix ListResourceTemplatesHandler issue. * Add remarks for empty protocol capability types * Keep JsonIgnore properties and mark them as Obsolete * Continue supporting Obsolete members * Update Obsolete messages to indicate the member will be removed * Add EditorBrowsable Never to the remaining obsolete members
This commit is contained in:
parent
39b0ed3c18
commit
5f992994c3
6 changed files with 1588 additions and 1161 deletions
|
|
@ -1,11 +1,13 @@
|
|||
using ModelContextProtocol.Protocol;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ModelContextProtocol.Server;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an instance of a Model Context Protocol (MCP) server that connects to and communicates with an MCP client.
|
||||
/// </summary>
|
||||
[Obsolete($"Use {nameof(McpServer)} instead.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[Obsolete($"Use {nameof(McpServer)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public interface IMcpServer : IMcpEndpoint
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
|
|
@ -26,7 +27,8 @@ public static class McpServerExtensions
|
|||
/// It allows detailed control over sampling parameters including messages, system prompt, temperature,
|
||||
/// and token limits.
|
||||
/// </remarks>
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.SampleAsync)} instead.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.SampleAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static ValueTask<CreateMessageResult> SampleAsync(
|
||||
this IMcpServer server, CreateMessageRequestParams request, CancellationToken cancellationToken = default)
|
||||
=> AsServerOrThrow(server).SampleAsync(request, cancellationToken);
|
||||
|
|
@ -46,7 +48,8 @@ public static class McpServerExtensions
|
|||
/// This method converts the provided chat messages into a format suitable for the sampling API,
|
||||
/// handling different content types such as text, images, and audio.
|
||||
/// </remarks>
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.SampleAsync)} instead.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774]
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.SampleAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static Task<ChatResponse> SampleAsync(
|
||||
this IMcpServer server,
|
||||
IEnumerable<ChatMessage> messages, ChatOptions? options = default, CancellationToken cancellationToken = default)
|
||||
|
|
@ -59,14 +62,16 @@ public static class McpServerExtensions
|
|||
/// <returns>The <see cref="IChatClient"/> that can be used to issue sampling requests to the client.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="server"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="InvalidOperationException">The client does not support sampling.</exception>
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.AsSamplingChatClient)} instead.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.AsSamplingChatClient)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static IChatClient AsSamplingChatClient(this IMcpServer server)
|
||||
=> AsServerOrThrow(server).AsSamplingChatClient();
|
||||
|
||||
/// <summary>Gets an <see cref="ILogger"/> on which logged messages will be sent as notifications to the client.</summary>
|
||||
/// <param name="server">The server to wrap as an <see cref="ILogger"/>.</param>
|
||||
/// <returns>An <see cref="ILogger"/> that can be used to log to the client..</returns>
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.AsSamplingChatClient)} instead.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.AsSamplingChatClient)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static ILoggerProvider AsClientLoggerProvider(this IMcpServer server)
|
||||
=> AsServerOrThrow(server).AsClientLoggerProvider();
|
||||
|
||||
|
|
@ -85,7 +90,8 @@ public static class McpServerExtensions
|
|||
/// navigated and accessed by the server. These resources might include file systems, databases,
|
||||
/// or other structured data sources that the client makes available through the protocol.
|
||||
/// </remarks>
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.RequestRootsAsync)} instead.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.RequestRootsAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static ValueTask<ListRootsResult> RequestRootsAsync(
|
||||
this IMcpServer server, ListRootsRequestParams request, CancellationToken cancellationToken = default)
|
||||
=> AsServerOrThrow(server).RequestRootsAsync(request, cancellationToken);
|
||||
|
|
@ -102,7 +108,8 @@ public static class McpServerExtensions
|
|||
/// <remarks>
|
||||
/// This method requires the client to support the elicitation capability.
|
||||
/// </remarks>
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.ElicitAsync)} instead.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.ElicitAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static ValueTask<ElicitResult> ElicitAsync(
|
||||
this IMcpServer server, ElicitRequestParams request, CancellationToken cancellationToken = default)
|
||||
=> AsServerOrThrow(server).ElicitAsync(request, cancellationToken);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ModelContextProtocol.Server;
|
||||
|
||||
|
|
@ -10,7 +11,8 @@ namespace ModelContextProtocol.Server;
|
|||
/// This is the recommended way to create <see cref="IMcpServer"/> instances.
|
||||
/// The factory handles proper initialization of server instances with the required dependencies.
|
||||
/// </remarks>
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.Create)} instead.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.Create)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static class McpServerFactory
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
177
src/ModelContextProtocol.Core/Server/McpServerHandlers.cs
Normal file
177
src/ModelContextProtocol.Core/Server/McpServerHandlers.cs
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
using ModelContextProtocol.Protocol;
|
||||
|
||||
namespace ModelContextProtocol.Server;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a container for handlers used in the creation of an MCP server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This class provides a centralized collection of delegates that implement various capabilities of the Model Context Protocol.
|
||||
/// Each handler in this class corresponds to a specific endpoint in the Model Context Protocol and
|
||||
/// is responsible for processing a particular type of message. The handlers are used to customize
|
||||
/// the behavior of the MCP server by providing implementations for the various protocol operations.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// When a client sends a message to the server, the appropriate handler is invoked to process it
|
||||
/// according to the protocol specification. Which handler is selected
|
||||
/// is done based on an ordinal, case-sensitive string comparison.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public sealed class McpServerHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for <see cref="RequestMethods.ToolsList"/> requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The handler should return a list of available tools when requested by a client.
|
||||
/// It supports pagination through the cursor mechanism, where the client can make
|
||||
/// repeated calls with the cursor returned by the previous call to retrieve more tools.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This handler works alongside any tools defined in the <see cref="McpServerTool"/> collection.
|
||||
/// Tools from both sources will be combined when returning results to clients.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public McpRequestHandler<ListToolsRequestParams, ListToolsResult>? ListToolsHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for <see cref="RequestMethods.ToolsCall"/> requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This handler is invoked when a client makes a call to a tool that isn't found in the <see cref="McpServerTool"/> collection.
|
||||
/// The handler should implement logic to execute the requested tool and return appropriate results.
|
||||
/// </remarks>
|
||||
public McpRequestHandler<CallToolRequestParams, CallToolResult>? CallToolHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for <see cref="RequestMethods.PromptsList"/> requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The handler should return a list of available prompts when requested by a client.
|
||||
/// It supports pagination through the cursor mechanism, where the client can make
|
||||
/// repeated calls with the cursor returned by the previous call to retrieve more prompts.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This handler works alongside any prompts defined in the <see cref="McpServerPrompt"/> collection.
|
||||
/// Prompts from both sources will be combined when returning results to clients.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public McpRequestHandler<ListPromptsRequestParams, ListPromptsResult>? ListPromptsHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for <see cref="RequestMethods.PromptsGet"/> requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This handler is invoked when a client requests details for a specific prompt that isn't found in the <see cref="McpServerPrompt"/> collection.
|
||||
/// The handler should implement logic to fetch or generate the requested prompt and return appropriate results.
|
||||
/// </remarks>
|
||||
public McpRequestHandler<GetPromptRequestParams, GetPromptResult>? GetPromptHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for <see cref="RequestMethods.ResourcesTemplatesList"/> requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The handler should return a list of available resource templates when requested by a client.
|
||||
/// It supports pagination through the cursor mechanism, where the client can make
|
||||
/// repeated calls with the cursor returned by the previous call to retrieve more resource templates.
|
||||
/// </remarks>
|
||||
public McpRequestHandler<ListResourceTemplatesRequestParams, ListResourceTemplatesResult>? ListResourceTemplatesHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for <see cref="RequestMethods.ResourcesList"/> requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The handler should return a list of available resources when requested by a client.
|
||||
/// It supports pagination through the cursor mechanism, where the client can make
|
||||
/// repeated calls with the cursor returned by the previous call to retrieve more resources.
|
||||
/// </remarks>
|
||||
public McpRequestHandler<ListResourcesRequestParams, ListResourcesResult>? ListResourcesHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for <see cref="RequestMethods.ResourcesRead"/> requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This handler is invoked when a client requests the content of a specific resource identified by its URI.
|
||||
/// The handler should implement logic to locate and retrieve the requested resource.
|
||||
/// </remarks>
|
||||
public McpRequestHandler<ReadResourceRequestParams, ReadResourceResult>? ReadResourceHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for <see cref="RequestMethods.CompletionComplete"/> requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This handler provides auto-completion suggestions for prompt arguments or resource references in the Model Context Protocol.
|
||||
/// The handler processes auto-completion requests, returning a list of suggestions based on the
|
||||
/// reference type and current argument value.
|
||||
/// </remarks>
|
||||
public McpRequestHandler<CompleteRequestParams, CompleteResult>? CompleteHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for <see cref="RequestMethods.ResourcesSubscribe"/> requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This handler is invoked when a client wants to receive notifications about changes to specific resources or resource patterns.
|
||||
/// The handler should implement logic to register the client's interest in the specified resources
|
||||
/// and set up the necessary infrastructure to send notifications when those resources change.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// After a successful subscription, the server should send resource change notifications to the client
|
||||
/// whenever a relevant resource is created, updated, or deleted.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public McpRequestHandler<SubscribeRequestParams, EmptyResult>? SubscribeToResourcesHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for <see cref="RequestMethods.ResourcesUnsubscribe"/> requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This handler is invoked when a client wants to stop receiving notifications about previously subscribed resources.
|
||||
/// The handler should implement logic to remove the client's subscriptions to the specified resources
|
||||
/// and clean up any associated resources.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// After a successful unsubscription, the server should no longer send resource change notifications
|
||||
/// to the client for the specified resources.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public McpRequestHandler<UnsubscribeRequestParams, EmptyResult>? UnsubscribeFromResourcesHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for <see cref="RequestMethods.LoggingSetLevel"/> requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This handler processes <see cref="RequestMethods.LoggingSetLevel"/> requests from clients. When set, it enables
|
||||
/// clients to control which log messages they receive by specifying a minimum severity threshold.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// After handling a level change request, the server typically begins sending log messages
|
||||
/// at or above the specified level to the client as notifications/message notifications.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public McpRequestHandler<SetLevelRequestParams, EmptyResult>? SetLoggingLevelHandler { get; set; }
|
||||
|
||||
/// <summary>Gets or sets notification handlers to register with the server.</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// When constructed, the server will enumerate these handlers once, which may contain multiple handlers per notification method key.
|
||||
/// The server will not re-enumerate the sequence after initialization.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Notification handlers allow the server to respond to client-sent notifications for specific methods.
|
||||
/// Each key in the collection is a notification method name, and each value is a callback that will be invoked
|
||||
/// when a notification with that method is received.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Handlers provided via <see cref="NotificationHandlers"/> will be registered with the server for the lifetime of the server.
|
||||
/// For transient handlers, <see cref="IMcpEndpoint.RegisterNotificationHandler"/> may be used to register a handler that can
|
||||
/// then be unregistered by disposing of the <see cref="IAsyncDisposable"/> returned from the method.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public IEnumerable<KeyValuePair<string, Func<JsonRpcNotification, CancellationToken, ValueTask>>>? NotificationHandlers { get; set; }
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
ConfigurePing();
|
||||
|
||||
// Register any notification handlers that were provided.
|
||||
if (options.Capabilities?.NotificationHandlers is { } notificationHandlers)
|
||||
if (options.Handlers.NotificationHandlers is { } notificationHandlers)
|
||||
{
|
||||
_notificationHandlers.RegisterRange(notificationHandlers);
|
||||
}
|
||||
|
|
@ -92,9 +92,9 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
// Now that everything has been configured, subscribe to any necessary notifications.
|
||||
if (transport is not StreamableHttpServerTransport streamableHttpTransport || streamableHttpTransport.Stateless is false)
|
||||
{
|
||||
Register(ServerOptions.Capabilities?.Tools?.ToolCollection, NotificationMethods.ToolListChangedNotification);
|
||||
Register(ServerOptions.Capabilities?.Prompts?.PromptCollection, NotificationMethods.PromptListChangedNotification);
|
||||
Register(ServerOptions.Capabilities?.Resources?.ResourceCollection, NotificationMethods.ResourceListChangedNotification);
|
||||
Register(ServerOptions.ToolCollection, NotificationMethods.ToolListChangedNotification);
|
||||
Register(ServerOptions.PromptCollection, NotificationMethods.PromptListChangedNotification);
|
||||
Register(ServerOptions.ResourceCollection, NotificationMethods.ResourceListChangedNotification);
|
||||
|
||||
void Register<TPrimitive>(McpServerPrimitiveCollection<TPrimitive>? collection, string notificationMethod)
|
||||
where TPrimitive : IMcpServerPrimitive
|
||||
|
|
@ -230,22 +230,26 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
|
||||
private void ConfigureCompletion(McpServerOptions options)
|
||||
{
|
||||
if (options.Capabilities?.Completions is not { } completionsCapability)
|
||||
var completeHandler = options.Handlers.CompleteHandler;
|
||||
var completionsCapability = options.Capabilities?.Completions;
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
completeHandler ??= completionsCapability?.CompleteHandler;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
if (completeHandler is null && completionsCapability is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var completeHandler = completionsCapability.CompleteHandler ?? (static async (_, __) => new CompleteResult());
|
||||
completeHandler ??= (static async (_, __) => new CompleteResult());
|
||||
completeHandler = BuildFilterPipeline(completeHandler, options.Filters.CompleteFilters);
|
||||
|
||||
ServerCapabilities.Completions = new()
|
||||
{
|
||||
CompleteHandler = completeHandler
|
||||
};
|
||||
ServerCapabilities.Completions = new();
|
||||
|
||||
SetHandler(
|
||||
RequestMethods.CompletionComplete,
|
||||
ServerCapabilities.Completions.CompleteHandler,
|
||||
completeHandler,
|
||||
McpJsonUtilities.JsonContext.Default.CompleteRequestParams,
|
||||
McpJsonUtilities.JsonContext.Default.CompleteResult);
|
||||
}
|
||||
|
|
@ -257,21 +261,38 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
|
||||
private void ConfigureResources(McpServerOptions options)
|
||||
{
|
||||
if (options.Capabilities?.Resources is not { } resourcesCapability)
|
||||
var listResourcesHandler = options.Handlers.ListResourcesHandler;
|
||||
var listResourceTemplatesHandler = options.Handlers.ListResourceTemplatesHandler;
|
||||
var readResourceHandler = options.Handlers.ReadResourceHandler;
|
||||
var subscribeHandler = options.Handlers.SubscribeToResourcesHandler;
|
||||
var unsubscribeHandler = options.Handlers.UnsubscribeFromResourcesHandler;
|
||||
var resources = options.ResourceCollection;
|
||||
var resourcesCapability = options.Capabilities?.Resources;
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
listResourcesHandler ??= resourcesCapability?.ListResourcesHandler;
|
||||
listResourceTemplatesHandler ??= resourcesCapability?.ListResourceTemplatesHandler;
|
||||
readResourceHandler ??= resourcesCapability?.ReadResourceHandler;
|
||||
subscribeHandler ??= resourcesCapability?.SubscribeToResourcesHandler;
|
||||
unsubscribeHandler ??= resourcesCapability?.UnsubscribeFromResourcesHandler;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
if (listResourcesHandler is null && listResourceTemplatesHandler is null && readResourceHandler is null &&
|
||||
subscribeHandler is null && unsubscribeHandler is null && resources is null &&
|
||||
resourcesCapability is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ServerCapabilities.Resources = new();
|
||||
|
||||
var listResourcesHandler = resourcesCapability.ListResourcesHandler ?? (static async (_, __) => new ListResourcesResult());
|
||||
var listResourceTemplatesHandler = resourcesCapability.ListResourceTemplatesHandler ?? (static async (_, __) => new ListResourceTemplatesResult());
|
||||
var readResourceHandler = resourcesCapability.ReadResourceHandler ?? (static async (request, _) => throw new McpException($"Unknown resource URI: '{request.Params?.Uri}'", McpErrorCode.InvalidParams));
|
||||
var subscribeHandler = resourcesCapability.SubscribeToResourcesHandler ?? (static async (_, __) => new EmptyResult());
|
||||
var unsubscribeHandler = resourcesCapability.UnsubscribeFromResourcesHandler ?? (static async (_, __) => new EmptyResult());
|
||||
var resources = resourcesCapability.ResourceCollection;
|
||||
var listChanged = resourcesCapability.ListChanged;
|
||||
var subscribe = resourcesCapability.Subscribe;
|
||||
listResourcesHandler ??= (static async (_, __) => new ListResourcesResult());
|
||||
listResourceTemplatesHandler ??= (static async (_, __) => new ListResourceTemplatesResult());
|
||||
readResourceHandler ??= (static async (request, _) => throw new McpException($"Unknown resource URI: '{request.Params?.Uri}'", McpErrorCode.InvalidParams));
|
||||
subscribeHandler ??= (static async (_, __) => new EmptyResult());
|
||||
unsubscribeHandler ??= (static async (_, __) => new EmptyResult());
|
||||
var listChanged = resourcesCapability?.ListChanged;
|
||||
var subscribe = resourcesCapability?.Subscribe;
|
||||
|
||||
// Handle resources provided via DI.
|
||||
if (resources is { IsEmpty: false })
|
||||
|
|
@ -376,12 +397,6 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
subscribeHandler = BuildFilterPipeline(subscribeHandler, options.Filters.SubscribeToResourcesFilters);
|
||||
unsubscribeHandler = BuildFilterPipeline(unsubscribeHandler, options.Filters.UnsubscribeFromResourcesFilters);
|
||||
|
||||
ServerCapabilities.Resources.ListResourcesHandler = listResourcesHandler;
|
||||
ServerCapabilities.Resources.ListResourceTemplatesHandler = listResourceTemplatesHandler;
|
||||
ServerCapabilities.Resources.ReadResourceHandler = readResourceHandler;
|
||||
ServerCapabilities.Resources.ResourceCollection = resources;
|
||||
ServerCapabilities.Resources.SubscribeToResourcesHandler = subscribeHandler;
|
||||
ServerCapabilities.Resources.UnsubscribeFromResourcesHandler = unsubscribeHandler;
|
||||
ServerCapabilities.Resources.ListChanged = listChanged;
|
||||
ServerCapabilities.Resources.Subscribe = subscribe;
|
||||
|
||||
|
|
@ -418,17 +433,27 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
|
||||
private void ConfigurePrompts(McpServerOptions options)
|
||||
{
|
||||
if (options.Capabilities?.Prompts is not { } promptsCapability)
|
||||
var listPromptsHandler = options.Handlers.ListPromptsHandler;
|
||||
var getPromptHandler = options.Handlers.GetPromptHandler;
|
||||
var prompts = options.PromptCollection;
|
||||
var promptsCapability = options.Capabilities?.Prompts;
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
listPromptsHandler ??= promptsCapability?.ListPromptsHandler;
|
||||
getPromptHandler ??= promptsCapability?.GetPromptHandler;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
if (listPromptsHandler is null && getPromptHandler is null && prompts is null &&
|
||||
promptsCapability is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ServerCapabilities.Prompts = new();
|
||||
|
||||
var listPromptsHandler = promptsCapability.ListPromptsHandler ?? (static async (_, __) => new ListPromptsResult());
|
||||
var getPromptHandler = promptsCapability.GetPromptHandler ?? (static async (request, _) => throw new McpException($"Unknown prompt: '{request.Params?.Name}'", McpErrorCode.InvalidParams));
|
||||
var prompts = promptsCapability.PromptCollection;
|
||||
var listChanged = promptsCapability.ListChanged;
|
||||
listPromptsHandler ??= (static async (_, __) => new ListPromptsResult());
|
||||
getPromptHandler ??= (static async (request, _) => throw new McpException($"Unknown prompt: '{request.Params?.Name}'", McpErrorCode.InvalidParams));
|
||||
var listChanged = promptsCapability?.ListChanged;
|
||||
|
||||
// Handle tools provided via DI by augmenting the handlers to incorporate them.
|
||||
if (prompts is { IsEmpty: false })
|
||||
|
|
@ -479,9 +504,6 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
return handler(request, cancellationToken);
|
||||
});
|
||||
|
||||
ServerCapabilities.Prompts.ListPromptsHandler = listPromptsHandler;
|
||||
ServerCapabilities.Prompts.GetPromptHandler = getPromptHandler;
|
||||
ServerCapabilities.Prompts.PromptCollection = prompts;
|
||||
ServerCapabilities.Prompts.ListChanged = listChanged;
|
||||
|
||||
SetHandler(
|
||||
|
|
@ -499,17 +521,27 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
|
||||
private void ConfigureTools(McpServerOptions options)
|
||||
{
|
||||
if (options.Capabilities?.Tools is not { } toolsCapability)
|
||||
var listToolsHandler = options.Handlers.ListToolsHandler;
|
||||
var callToolHandler = options.Handlers.CallToolHandler;
|
||||
var tools = options.ToolCollection;
|
||||
var toolsCapability = options.Capabilities?.Tools;
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
listToolsHandler ??= toolsCapability?.ListToolsHandler;
|
||||
callToolHandler ??= toolsCapability?.CallToolHandler;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
if (listToolsHandler is null && callToolHandler is null && tools is null &&
|
||||
toolsCapability is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ServerCapabilities.Tools = new();
|
||||
|
||||
var listToolsHandler = toolsCapability.ListToolsHandler ?? (static async (_, __) => new ListToolsResult());
|
||||
var callToolHandler = toolsCapability.CallToolHandler ?? (static async (request, _) => throw new McpException($"Unknown tool: '{request.Params?.Name}'", McpErrorCode.InvalidParams));
|
||||
var tools = toolsCapability.ToolCollection;
|
||||
var listChanged = toolsCapability.ListChanged;
|
||||
listToolsHandler ??= (static async (_, __) => new ListToolsResult());
|
||||
callToolHandler ??= (static async (request, _) => throw new McpException($"Unknown tool: '{request.Params?.Name}'", McpErrorCode.InvalidParams));
|
||||
var listChanged = toolsCapability?.ListChanged;
|
||||
|
||||
// Handle tools provided via DI by augmenting the handlers to incorporate them.
|
||||
if (tools is { IsEmpty: false })
|
||||
|
|
@ -591,9 +623,6 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
}
|
||||
});
|
||||
|
||||
ServerCapabilities.Tools.ListToolsHandler = listToolsHandler;
|
||||
ServerCapabilities.Tools.CallToolHandler = callToolHandler;
|
||||
ServerCapabilities.Tools.ToolCollection = tools;
|
||||
ServerCapabilities.Tools.ListChanged = listChanged;
|
||||
|
||||
SetHandler(
|
||||
|
|
@ -612,7 +641,11 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
private void ConfigureLogging(McpServerOptions options)
|
||||
{
|
||||
// We don't require that the handler be provided, as we always store the provided log level to the server.
|
||||
var setLoggingLevelHandler = options.Capabilities?.Logging?.SetLoggingLevelHandler;
|
||||
var setLoggingLevelHandler = options.Handlers.SetLoggingLevelHandler;
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
setLoggingLevelHandler ??= options.Capabilities?.Logging?.SetLoggingLevelHandler;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
// Apply filters to the handler
|
||||
if (setLoggingLevelHandler is not null)
|
||||
|
|
@ -621,7 +654,6 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
}
|
||||
|
||||
ServerCapabilities.Logging = new();
|
||||
ServerCapabilities.Logging.SetLoggingLevelHandler = setLoggingLevelHandler;
|
||||
|
||||
_requestHandlers.Set(
|
||||
RequestMethods.LoggingSetLevel,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ namespace ModelContextProtocol.Server;
|
|||
/// </summary>
|
||||
public sealed class McpServerOptions
|
||||
{
|
||||
private McpServerHandlers? _handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets information about this server implementation, including its name and version.
|
||||
/// </summary>
|
||||
|
|
@ -89,4 +91,67 @@ public sealed class McpServerOptions
|
|||
/// added will be the outermost (first to execute).
|
||||
/// </remarks>
|
||||
public McpServerFilters Filters { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the container of handlers used by the server for processing protocol messages.
|
||||
/// </summary>
|
||||
public McpServerHandlers Handlers
|
||||
{
|
||||
get => _handlers ??= new();
|
||||
set
|
||||
{
|
||||
Throw.IfNull(value);
|
||||
_handlers = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a collection of tools served by the server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Tools specified via <see cref="ToolCollection"/> augment the <see cref="McpServerHandlers.ListToolsHandler"/> and
|
||||
/// <see cref="McpServerHandlers.CallToolHandler"/>, if provided. ListTools requests will output information about every tool
|
||||
/// in <see cref="ToolCollection"/> and then also any tools output by <see cref="McpServerHandlers.ListToolsHandler"/>, if it's
|
||||
/// non-<see langword="null"/>. CallTool requests will first check <see cref="ToolCollection"/> for the tool
|
||||
/// being requested, and if the tool is not found in the <see cref="ToolCollection"/>, any specified <see cref="McpServerHandlers.CallToolHandler"/>
|
||||
/// will be invoked as a fallback.
|
||||
/// </remarks>
|
||||
public McpServerPrimitiveCollection<McpServerTool>? ToolCollection { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a collection of resources served by the server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Resources specified via <see cref="ResourceCollection"/> augment the <see cref="McpServerHandlers.ListResourcesHandler"/>, <see cref="McpServerHandlers.ListResourceTemplatesHandler"/>
|
||||
/// and <see cref="McpServerHandlers.ReadResourceHandler"/> handlers, if provided. Resources with template expressions in their URI templates are considered resource templates
|
||||
/// and are listed via ListResourceTemplate, whereas resources without template parameters are considered static resources and are listed with ListResources.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// ReadResource requests will first check the <see cref="ResourceCollection"/> for the exact resource being requested. If no match is found, they'll proceed to
|
||||
/// try to match the resource against each resource template in <see cref="ResourceCollection"/>. If no match is still found, the request will fall back to
|
||||
/// any handler registered for <see cref="McpServerHandlers.ReadResourceHandler"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public McpServerResourceCollection? ResourceCollection { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a collection of prompts that will be served by the server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The <see cref="PromptCollection"/> contains the predefined prompts that clients can request from the server.
|
||||
/// This collection works in conjunction with <see cref="McpServerHandlers.ListPromptsHandler"/> and <see cref="McpServerHandlers.GetPromptHandler"/>
|
||||
/// when those are provided:
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// - For <see cref="RequestMethods.PromptsList"/> requests: The server returns all prompts from this collection
|
||||
/// plus any additional prompts provided by the <see cref="McpServerHandlers.ListPromptsHandler"/> if it's set.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// - For <see cref="RequestMethods.PromptsGet"/> requests: The server first checks this collection for the requested prompt.
|
||||
/// If not found, it will invoke the <see cref="McpServerHandlers.GetPromptHandler"/> as a fallback if one is set.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public McpServerPrimitiveCollection<McpServerPrompt>? PromptCollection { get; set; }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue