diff --git a/src/ModelContextProtocol/Client/McpClientOptions.cs b/src/ModelContextProtocol/Client/McpClientOptions.cs index 9b070e92..de61f18f 100644 --- a/src/ModelContextProtocol/Client/McpClientOptions.cs +++ b/src/ModelContextProtocol/Client/McpClientOptions.cs @@ -7,25 +7,25 @@ namespace ModelContextProtocol.Client; /// protocol version. /// See the protocol specification for details on capability negotiation /// -public record McpClientOptions +public class McpClientOptions { /// /// Information about this client implementation. /// - public required Implementation ClientInfo { get; init; } + public required Implementation ClientInfo { get; set; } /// /// Client capabilities to advertise to the server. /// - public ClientCapabilities? Capabilities { get; init; } + public ClientCapabilities? Capabilities { get; set; } /// /// Protocol version to request from the server. /// - public string ProtocolVersion { get; init; } = "2024-11-05"; + public string ProtocolVersion { get; set; } = "2024-11-05"; /// /// Timeout for initialization sequence. /// - public TimeSpan InitializationTimeout { get; init; } = TimeSpan.FromSeconds(60); + public TimeSpan InitializationTimeout { get; set; } = TimeSpan.FromSeconds(60); } diff --git a/src/ModelContextProtocol/Configuration/McpServerOptionsSetup.cs b/src/ModelContextProtocol/Configuration/McpServerOptionsSetup.cs index 687a534b..c2ebbf62 100644 --- a/src/ModelContextProtocol/Configuration/McpServerOptionsSetup.cs +++ b/src/ModelContextProtocol/Configuration/McpServerOptionsSetup.cs @@ -25,22 +25,14 @@ public void Configure(McpServerOptions options) // Configure the option's server information based on the current process, // if it otherwise lacks server information. - var assemblyName = (Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly()).GetName(); - if (options.ServerInfo is not { } serverInfo || - serverInfo.Name is null || - serverInfo.Version is null) + if (options.ServerInfo is not { } serverInfo) { - options.ServerInfo = options.ServerInfo is null ? - new() - { - Name = assemblyName.Name ?? "McpServer", - Version = assemblyName.Version?.ToString() ?? "1.0.0", - } : - options.ServerInfo with - { - Name = options.ServerInfo.Name ?? assemblyName.Name ?? "McpServer", - Version = options.ServerInfo.Version ?? assemblyName.Version?.ToString() ?? "1.0.0", - }; + var assemblyName = (Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly()).GetName(); + options.ServerInfo = new() + { + Name = assemblyName.Name ?? "McpServer", + Version = assemblyName.Version?.ToString() ?? "1.0.0", + }; } // Collect all of the provided tools into a tools collection. If the options already has @@ -55,14 +47,9 @@ options.ServerInfo with if (!toolsCollection.IsEmpty) { - options.Capabilities = options.Capabilities is null ? - new() { Tools = new() { ToolCollection = toolsCollection } } : - options.Capabilities with - { - Tools = options.Capabilities.Tools is null ? - new() { ToolCollection = toolsCollection } : - options.Capabilities.Tools with { ToolCollection = toolsCollection }, - }; + options.Capabilities ??= new(); + options.Capabilities.Tools ??= new(); + options.Capabilities.Tools.ToolCollection = toolsCollection; } // Apply custom server handlers. diff --git a/src/ModelContextProtocol/Protocol/Types/Capabilities.cs b/src/ModelContextProtocol/Protocol/Types/Capabilities.cs index a82afee0..9be4fdd5 100644 --- a/src/ModelContextProtocol/Protocol/Types/Capabilities.cs +++ b/src/ModelContextProtocol/Protocol/Types/Capabilities.cs @@ -7,62 +7,62 @@ namespace ModelContextProtocol.Protocol.Types; /// Represents the capabilities that a client may support. /// See the schema for details /// -public record ClientCapabilities +public class ClientCapabilities { /// /// Experimental, non-standard capabilities that the client supports. /// [JsonPropertyName("experimental")] - public Dictionary? Experimental { get; init; } + public Dictionary? Experimental { get; set; } /// /// Present if the client supports listing roots. /// [JsonPropertyName("roots")] - public RootsCapability? Roots { get; init; } + public RootsCapability? Roots { get; set; } /// /// Present if the client supports sampling from an LLM. /// [JsonPropertyName("sampling")] - public SamplingCapability? Sampling { get; init; } + public SamplingCapability? Sampling { get; set; } } /// /// Represents the roots capability configuration. /// See the schema for details /// -public record RootsCapability +public class RootsCapability { /// /// Whether the client supports notifications for changes to the roots list. /// [JsonPropertyName("listChanged")] - public bool? ListChanged { get; init; } + public bool? ListChanged { get; set; } /// Gets or sets the handler for sampling requests. [JsonIgnore] - public Func>? RootsHandler { get; init; } + public Func>? RootsHandler { get; set; } } /// /// Represents the sampling capability configuration. /// See the schema for details /// -public record SamplingCapability +public class SamplingCapability { // Currently empty in the spec, but may be extended in the future /// Gets or sets the handler for sampling requests. [JsonIgnore] - public Func>? SamplingHandler { get; init; } + public Func>? SamplingHandler { get; set; } } /// /// Represents the logging capability configuration. /// See the schema for details /// -public record LoggingCapability +public class LoggingCapability { // Currently empty in the spec, but may be extended in the future @@ -71,106 +71,106 @@ public record LoggingCapability /// Gets or sets the handler for set logging level requests. /// [JsonIgnore] - public Func, CancellationToken, Task>? SetLoggingLevelHandler { get; init; } + public Func, CancellationToken, Task>? SetLoggingLevelHandler { get; set; } } /// /// Represents the prompts capability configuration. /// See the schema for details /// -public record PromptsCapability +public class PromptsCapability { /// /// Whether this server supports notifications for changes to the prompt list. /// [JsonPropertyName("listChanged")] - public bool? ListChanged { get; init; } + public bool? ListChanged { get; set; } /// /// Gets or sets the handler for list prompts requests. /// [JsonIgnore] - public Func, CancellationToken, Task>? ListPromptsHandler { get; init; } + public Func, CancellationToken, Task>? ListPromptsHandler { get; set; } /// /// Gets or sets the handler for get prompt requests. /// [JsonIgnore] - public Func, CancellationToken, Task>? GetPromptHandler { get; init; } + public Func, CancellationToken, Task>? GetPromptHandler { get; set; } } /// /// Represents the resources capability configuration. /// See the schema for details /// -public record ResourcesCapability +public class ResourcesCapability { /// /// Whether this server supports subscribing to resource updates. /// [JsonPropertyName("subscribe")] - public bool? Subscribe { get; init; } + public bool? Subscribe { get; set; } /// /// Whether this server supports notifications for changes to the resource list. /// [JsonPropertyName("listChanged")] - public bool? ListChanged { get; init; } + public bool? ListChanged { get; set; } /// /// Gets or sets the handler for list resource templates requests. /// [JsonIgnore] - public Func, CancellationToken, Task>? ListResourceTemplatesHandler { get; init; } + public Func, CancellationToken, Task>? ListResourceTemplatesHandler { get; set; } /// /// Gets or sets the handler for list resources requests. /// [JsonIgnore] - public Func, CancellationToken, Task>? ListResourcesHandler { get; init; } + public Func, CancellationToken, Task>? ListResourcesHandler { get; set; } /// /// Gets or sets the handler for read resources requests. /// [JsonIgnore] - public Func, CancellationToken, Task>? ReadResourceHandler { get; init; } + public Func, CancellationToken, Task>? ReadResourceHandler { get; set; } /// /// Gets or sets the handler for subscribe to resources messages. /// [JsonIgnore] - public Func, CancellationToken, Task>? SubscribeToResourcesHandler { get; init; } + public Func, CancellationToken, Task>? SubscribeToResourcesHandler { get; set; } /// /// Gets or sets the handler for unsubscribe from resources messages. /// [JsonIgnore] - public Func, CancellationToken, Task>? UnsubscribeFromResourcesHandler { get; init; } + public Func, CancellationToken, Task>? UnsubscribeFromResourcesHandler { get; set; } } /// /// Represents the tools capability configuration. /// See the schema for details /// -public record ToolsCapability +public class ToolsCapability { /// /// Gets or sets whether this server supports notifications for changes to the tool list. /// [JsonPropertyName("listChanged")] - public bool? ListChanged { get; init; } + public bool? ListChanged { get; set; } /// /// Gets or sets the handler for list tools requests. /// [JsonIgnore] - public Func, CancellationToken, Task>? ListToolsHandler { get; init; } + public Func, CancellationToken, Task>? ListToolsHandler { get; set; } /// /// Gets or sets the handler for call tool requests. /// [JsonIgnore] - public Func, CancellationToken, Task>? CallToolHandler { get; init; } + public Func, CancellationToken, Task>? CallToolHandler { get; set; } /// Gets or sets a collection of tools served by the server. /// @@ -182,5 +182,5 @@ public record ToolsCapability /// will be invoked as a fallback. /// [JsonIgnore] - public McpServerToolCollection? ToolCollection { get; init; } + public McpServerToolCollection? ToolCollection { get; set; } } \ No newline at end of file diff --git a/src/ModelContextProtocol/Protocol/Types/Implementation.cs b/src/ModelContextProtocol/Protocol/Types/Implementation.cs index 23f44552..81fe8c01 100644 --- a/src/ModelContextProtocol/Protocol/Types/Implementation.cs +++ b/src/ModelContextProtocol/Protocol/Types/Implementation.cs @@ -6,17 +6,17 @@ namespace ModelContextProtocol.Protocol.Types; /// Describes the name and version of an MCP implementation. /// See the schema for details /// -public record Implementation +public class Implementation { /// /// Name of the implementation. /// [JsonPropertyName("name")] - public required string Name { get; init; } + public required string Name { get; set; } /// /// Version of the implementation. /// [JsonPropertyName("version")] - public required string Version { get; init; } + public required string Version { get; set; } } \ No newline at end of file diff --git a/src/ModelContextProtocol/Protocol/Types/ServerCapabilities.cs b/src/ModelContextProtocol/Protocol/Types/ServerCapabilities.cs index 15b486f6..6890674c 100644 --- a/src/ModelContextProtocol/Protocol/Types/ServerCapabilities.cs +++ b/src/ModelContextProtocol/Protocol/Types/ServerCapabilities.cs @@ -6,35 +6,35 @@ namespace ModelContextProtocol.Protocol.Types; /// Represents the capabilities that a server may support. /// See the schema for details /// -public record ServerCapabilities +public class ServerCapabilities { /// /// Experimental, non-standard capabilities that the server supports. /// [JsonPropertyName("experimental")] - public Dictionary? Experimental { get; init; } + public Dictionary? Experimental { get; set; } /// /// Present if the server supports sending log messages to the client. /// [JsonPropertyName("logging")] - public LoggingCapability? Logging { get; init; } + public LoggingCapability? Logging { get; set; } /// /// Present if the server offers any prompt templates. /// [JsonPropertyName("prompts")] - public PromptsCapability? Prompts { get; init; } + public PromptsCapability? Prompts { get; set; } /// /// Present if the server offers any resources to read. /// [JsonPropertyName("resources")] - public ResourcesCapability? Resources { get; init; } + public ResourcesCapability? Resources { get; set; } /// /// Present if the server offers any tools to call. /// [JsonPropertyName("tools")] - public ToolsCapability? Tools { get; init; } + public ToolsCapability? Tools { get; set; } } diff --git a/src/ModelContextProtocol/Server/McpServer.cs b/src/ModelContextProtocol/Server/McpServer.cs index a7779947..64e332d2 100644 --- a/src/ModelContextProtocol/Server/McpServer.cs +++ b/src/ModelContextProtocol/Server/McpServer.cs @@ -263,25 +263,14 @@ private void SetToolsHandler(ref McpServerOptions options) return tool.InvokeAsync(request, cancellationToken); }; - toolsCapability = toolsCapability is null ? - new() - { - CallToolHandler = callToolHandler, - ListToolsHandler = listToolsHandler, - ToolCollection = tools, - ListChanged = true, - } : - toolsCapability with - { - CallToolHandler = callToolHandler, - ListToolsHandler = listToolsHandler, - ToolCollection = tools, - ListChanged = true, - }; - - options.Capabilities = options.Capabilities is null ? - new() { Tools = toolsCapability } : - options.Capabilities with { Tools = toolsCapability }; + toolsCapability ??= new(); + toolsCapability.CallToolHandler = callToolHandler; + toolsCapability.ListToolsHandler = listToolsHandler; + toolsCapability.ToolCollection = tools; + toolsCapability.ListChanged = true; + + options.Capabilities ??= new(); + options.Capabilities.Tools = toolsCapability; tools.Changed += delegate { diff --git a/src/ModelContextProtocol/Server/McpServerHandlers.cs b/src/ModelContextProtocol/Server/McpServerHandlers.cs index 32deca49..e472a7ae 100644 --- a/src/ModelContextProtocol/Server/McpServerHandlers.cs +++ b/src/ModelContextProtocol/Server/McpServerHandlers.cs @@ -72,91 +72,47 @@ internal void OverwriteWithSetHandlers(McpServerOptions options) PromptsCapability? promptsCapability = options.Capabilities?.Prompts; if (ListPromptsHandler is not null || GetPromptHandler is not null) { - promptsCapability = promptsCapability is null ? - new() - { - ListPromptsHandler = ListPromptsHandler, - GetPromptHandler = GetPromptHandler, - } : - promptsCapability with - { - ListPromptsHandler = ListPromptsHandler ?? promptsCapability.ListPromptsHandler, - GetPromptHandler = GetPromptHandler ?? promptsCapability.GetPromptHandler, - }; + promptsCapability ??= new(); + promptsCapability.ListPromptsHandler = ListPromptsHandler ?? promptsCapability.ListPromptsHandler; + promptsCapability.GetPromptHandler = GetPromptHandler ?? promptsCapability.GetPromptHandler; } ResourcesCapability? resourcesCapability = options.Capabilities?.Resources; if (ListResourcesHandler is not null || ReadResourceHandler is not null) { - resourcesCapability = resourcesCapability is null ? - new() - { - ListResourceTemplatesHandler = ListResourceTemplatesHandler, - ListResourcesHandler = ListResourcesHandler, - ReadResourceHandler = ReadResourceHandler - } : - resourcesCapability with - { - ListResourceTemplatesHandler = ListResourceTemplatesHandler ?? resourcesCapability.ListResourceTemplatesHandler, - ListResourcesHandler = ListResourcesHandler ?? resourcesCapability.ListResourcesHandler, - ReadResourceHandler = ReadResourceHandler ?? resourcesCapability.ReadResourceHandler - }; + resourcesCapability ??= new(); + resourcesCapability.ListResourceTemplatesHandler = ListResourceTemplatesHandler ?? resourcesCapability.ListResourceTemplatesHandler; + resourcesCapability.ListResourcesHandler = ListResourcesHandler ?? resourcesCapability.ListResourcesHandler; + resourcesCapability.ReadResourceHandler = ReadResourceHandler ?? resourcesCapability.ReadResourceHandler; if (SubscribeToResourcesHandler is not null || UnsubscribeFromResourcesHandler is not null) { - resourcesCapability = resourcesCapability with - { - SubscribeToResourcesHandler = SubscribeToResourcesHandler ?? resourcesCapability.SubscribeToResourcesHandler, - UnsubscribeFromResourcesHandler = UnsubscribeFromResourcesHandler ?? resourcesCapability.UnsubscribeFromResourcesHandler, - Subscribe = true - }; + resourcesCapability.SubscribeToResourcesHandler = SubscribeToResourcesHandler ?? resourcesCapability.SubscribeToResourcesHandler; + resourcesCapability.UnsubscribeFromResourcesHandler = UnsubscribeFromResourcesHandler ?? resourcesCapability.UnsubscribeFromResourcesHandler; + resourcesCapability.Subscribe = true; } } ToolsCapability? toolsCapability = options.Capabilities?.Tools; if (ListToolsHandler is not null || CallToolHandler is not null) { - toolsCapability = toolsCapability is null ? - new() - { - ListToolsHandler = ListToolsHandler, - CallToolHandler = CallToolHandler, - } : - toolsCapability with - { - ListToolsHandler = ListToolsHandler ?? toolsCapability.ListToolsHandler, - CallToolHandler = CallToolHandler ?? toolsCapability.CallToolHandler, - }; + toolsCapability ??= new(); + toolsCapability.ListToolsHandler = ListToolsHandler ?? toolsCapability.ListToolsHandler; + toolsCapability.CallToolHandler = CallToolHandler ?? toolsCapability.CallToolHandler; } LoggingCapability? loggingCapability = options.Capabilities?.Logging; if (SetLoggingLevelHandler is not null) { - loggingCapability = loggingCapability is null ? - new() - { - SetLoggingLevelHandler = SetLoggingLevelHandler, - } : - loggingCapability with - { - SetLoggingLevelHandler = SetLoggingLevelHandler ?? loggingCapability.SetLoggingLevelHandler, - }; + loggingCapability ??= new(); + loggingCapability.SetLoggingLevelHandler = SetLoggingLevelHandler; } - options.Capabilities = options.Capabilities is null ? - new() - { - Prompts = promptsCapability, - Resources = resourcesCapability, - Tools = toolsCapability, - } : - options.Capabilities with - { - Prompts = promptsCapability, - Resources = resourcesCapability, - Tools = toolsCapability, - }; + options.Capabilities ??= new(); + options.Capabilities.Prompts = promptsCapability; + options.Capabilities.Resources = resourcesCapability; + options.Capabilities.Tools = toolsCapability; options.GetCompletionHandler = GetCompletionHandler ?? options.GetCompletionHandler; } diff --git a/tests/ModelContextProtocol.Tests/SseServerIntegrationTestFixture.cs b/tests/ModelContextProtocol.Tests/SseServerIntegrationTestFixture.cs index 2702ed83..75c727d1 100644 --- a/tests/ModelContextProtocol.Tests/SseServerIntegrationTestFixture.cs +++ b/tests/ModelContextProtocol.Tests/SseServerIntegrationTestFixture.cs @@ -15,7 +15,6 @@ public class SseServerIntegrationTestFixture : IAsyncDisposable private readonly DelegatingTestOutputHelper _delegatingTestOutputHelper = new(); private readonly ILoggerFactory _redirectingLoggerFactory; - public McpClientOptions DefaultOptions { get; } public McpServerConfig DefaultConfig { get; } public SseServerIntegrationTestFixture() @@ -26,11 +25,6 @@ public SseServerIntegrationTestFixture() builder.AddProvider(new XunitLoggerProvider(_delegatingTestOutputHelper)); }); - DefaultOptions = new() - { - ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" }, - }; - DefaultConfig = new McpServerConfig { Id = "test_server", @@ -43,6 +37,11 @@ public SseServerIntegrationTestFixture() _serverTask = Program.MainAsync([], _redirectingLoggerFactory, _stopCts.Token); } + public static McpClientOptions CreateDefaultClientOptions() => new() + { + ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" }, + }; + public void Initialize(ITestOutputHelper output) { _delegatingTestOutputHelper.CurrentTestOutputHelper = output; diff --git a/tests/ModelContextProtocol.Tests/SseServerIntegrationTests.cs b/tests/ModelContextProtocol.Tests/SseServerIntegrationTests.cs index 767005ea..b834f843 100644 --- a/tests/ModelContextProtocol.Tests/SseServerIntegrationTests.cs +++ b/tests/ModelContextProtocol.Tests/SseServerIntegrationTests.cs @@ -19,7 +19,7 @@ private Task GetClientAsync(McpClientOptions? options = null) { return McpClientFactory.CreateAsync( _fixture.DefaultConfig, - options ?? _fixture.DefaultOptions, + options ?? SseServerIntegrationTestFixture.CreateDefaultClientOptions(), loggerFactory: LoggerFactory); } @@ -201,28 +201,22 @@ public async Task Sampling_Sse_TestServer() // Set up the sampling handler int samplingHandlerCalls = 0; #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - var options = _fixture.DefaultOptions with + var options = SseServerIntegrationTestFixture.CreateDefaultClientOptions(); + options.Capabilities ??= new(); + options.Capabilities.Sampling ??= new(); + options.Capabilities.Sampling.SamplingHandler = async (_, _) => { - Capabilities = new() + samplingHandlerCalls++; + return new CreateMessageResult { - Sampling = new() + Model = "test-model", + Role = "assistant", + Content = new Content { - SamplingHandler = async (_, _) => - { - samplingHandlerCalls++; - return new CreateMessageResult - { - Model = "test-model", - Role = "assistant", - Content = new Content - { - Type = "text", - Text = "Test response" - } - }; - } + Type = "text", + Text = "Test response" } - } + }; }; var client = await GetClientAsync(options); #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously