Make options types fully mutable (#107)

This commit is contained in:
Stephen Halter 2025-03-26 10:39:49 -07:00 • committed by GitHub
commit 95a017a03a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 97 additions and 172 deletions

View file

@ -7,25 +7,25 @@ namespace ModelContextProtocol.Client;
/// protocol version. /// protocol version.
/// <see href="https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle/">See the protocol specification for details on capability negotiation</see> /// <see href="https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle/">See the protocol specification for details on capability negotiation</see>
/// </summary> /// </summary>
public record McpClientOptions public class McpClientOptions
{ {
/// <summary> /// <summary>
/// Information about this client implementation. /// Information about this client implementation.
/// </summary> /// </summary>
public required Implementation ClientInfo { get; init; } public required Implementation ClientInfo { get; set; }
/// <summary> /// <summary>
/// Client capabilities to advertise to the server. /// Client capabilities to advertise to the server.
/// </summary> /// </summary>
public ClientCapabilities? Capabilities { get; init; } public ClientCapabilities? Capabilities { get; set; }
/// <summary> /// <summary>
/// Protocol version to request from the server. /// Protocol version to request from the server.
/// </summary> /// </summary>
public string ProtocolVersion { get; init; } = "2024-11-05"; public string ProtocolVersion { get; set; } = "2024-11-05";
/// <summary> /// <summary>
/// Timeout for initialization sequence. /// Timeout for initialization sequence.
/// </summary> /// </summary>
public TimeSpan InitializationTimeout { get; init; } = TimeSpan.FromSeconds(60); public TimeSpan InitializationTimeout { get; set; } = TimeSpan.FromSeconds(60);
} }

View file

@ -25,22 +25,14 @@ internal sealed class McpServerOptionsSetup(
// Configure the option's server information based on the current process, // Configure the option's server information based on the current process,
// if it otherwise lacks server information. // if it otherwise lacks server information.
var assemblyName = (Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly()).GetName(); if (options.ServerInfo is not { } serverInfo)
if (options.ServerInfo is not { } serverInfo ||
serverInfo.Name is null ||
serverInfo.Version is null)
{ {
options.ServerInfo = options.ServerInfo is null ? var assemblyName = (Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly()).GetName();
new() options.ServerInfo = new()
{ {
Name = assemblyName.Name ?? "McpServer", Name = assemblyName.Name ?? "McpServer",
Version = assemblyName.Version?.ToString() ?? "1.0.0", 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",
};
} }
// Collect all of the provided tools into a tools collection. If the options already has // Collect all of the provided tools into a tools collection. If the options already has
@ -55,14 +47,9 @@ internal sealed class McpServerOptionsSetup(
if (!toolsCollection.IsEmpty) if (!toolsCollection.IsEmpty)
{ {
options.Capabilities = options.Capabilities is null ? options.Capabilities ??= new();
new() { Tools = new() { ToolCollection = toolsCollection } } : options.Capabilities.Tools ??= new();
options.Capabilities with options.Capabilities.Tools.ToolCollection = toolsCollection;
{
Tools = options.Capabilities.Tools is null ?
new() { ToolCollection = toolsCollection } :
options.Capabilities.Tools with { ToolCollection = toolsCollection },
};
} }
// Apply custom server handlers. // Apply custom server handlers.

View file

@ -7,62 +7,62 @@ namespace ModelContextProtocol.Protocol.Types;
/// Represents the capabilities that a client may support. /// Represents the capabilities that a client may support.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see> /// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary> /// </summary>
public record ClientCapabilities public class ClientCapabilities
{ {
/// <summary> /// <summary>
/// Experimental, non-standard capabilities that the client supports. /// Experimental, non-standard capabilities that the client supports.
/// </summary> /// </summary>
[JsonPropertyName("experimental")] [JsonPropertyName("experimental")]
public Dictionary<string, object>? Experimental { get; init; } public Dictionary<string, object>? Experimental { get; set; }
/// <summary> /// <summary>
/// Present if the client supports listing roots. /// Present if the client supports listing roots.
/// </summary> /// </summary>
[JsonPropertyName("roots")] [JsonPropertyName("roots")]
public RootsCapability? Roots { get; init; } public RootsCapability? Roots { get; set; }
/// <summary> /// <summary>
/// Present if the client supports sampling from an LLM. /// Present if the client supports sampling from an LLM.
/// </summary> /// </summary>
[JsonPropertyName("sampling")] [JsonPropertyName("sampling")]
public SamplingCapability? Sampling { get; init; } public SamplingCapability? Sampling { get; set; }
} }
/// <summary> /// <summary>
/// Represents the roots capability configuration. /// Represents the roots capability configuration.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see> /// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary> /// </summary>
public record RootsCapability public class RootsCapability
{ {
/// <summary> /// <summary>
/// Whether the client supports notifications for changes to the roots list. /// Whether the client supports notifications for changes to the roots list.
/// </summary> /// </summary>
[JsonPropertyName("listChanged")] [JsonPropertyName("listChanged")]
public bool? ListChanged { get; init; } public bool? ListChanged { get; set; }
/// <summary>Gets or sets the handler for sampling requests.</summary> /// <summary>Gets or sets the handler for sampling requests.</summary>
[JsonIgnore] [JsonIgnore]
public Func<ListRootsRequestParams?, CancellationToken, Task<ListRootsResult>>? RootsHandler { get; init; } public Func<ListRootsRequestParams?, CancellationToken, Task<ListRootsResult>>? RootsHandler { get; set; }
} }
/// <summary> /// <summary>
/// Represents the sampling capability configuration. /// Represents the sampling capability configuration.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see> /// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary> /// </summary>
public record SamplingCapability public class SamplingCapability
{ {
// Currently empty in the spec, but may be extended in the future // Currently empty in the spec, but may be extended in the future
/// <summary>Gets or sets the handler for sampling requests.</summary> /// <summary>Gets or sets the handler for sampling requests.</summary>
[JsonIgnore] [JsonIgnore]
public Func<CreateMessageRequestParams?, CancellationToken, Task<CreateMessageResult>>? SamplingHandler { get; init; } public Func<CreateMessageRequestParams?, CancellationToken, Task<CreateMessageResult>>? SamplingHandler { get; set; }
} }
/// <summary> /// <summary>
/// Represents the logging capability configuration. /// Represents the logging capability configuration.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see> /// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary> /// </summary>
public record LoggingCapability public class LoggingCapability
{ {
// Currently empty in the spec, but may be extended in the future // 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. /// Gets or sets the handler for set logging level requests.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public Func<RequestContext<SetLevelRequestParams>, CancellationToken, Task<EmptyResult>>? SetLoggingLevelHandler { get; init; } public Func<RequestContext<SetLevelRequestParams>, CancellationToken, Task<EmptyResult>>? SetLoggingLevelHandler { get; set; }
} }
/// <summary> /// <summary>
/// Represents the prompts capability configuration. /// Represents the prompts capability configuration.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see> /// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary> /// </summary>
public record PromptsCapability public class PromptsCapability
{ {
/// <summary> /// <summary>
/// Whether this server supports notifications for changes to the prompt list. /// Whether this server supports notifications for changes to the prompt list.
/// </summary> /// </summary>
[JsonPropertyName("listChanged")] [JsonPropertyName("listChanged")]
public bool? ListChanged { get; init; } public bool? ListChanged { get; set; }
/// <summary> /// <summary>
/// Gets or sets the handler for list prompts requests. /// Gets or sets the handler for list prompts requests.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public Func<RequestContext<ListPromptsRequestParams>, CancellationToken, Task<ListPromptsResult>>? ListPromptsHandler { get; init; } public Func<RequestContext<ListPromptsRequestParams>, CancellationToken, Task<ListPromptsResult>>? ListPromptsHandler { get; set; }
/// <summary> /// <summary>
/// Gets or sets the handler for get prompt requests. /// Gets or sets the handler for get prompt requests.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public Func<RequestContext<GetPromptRequestParams>, CancellationToken, Task<GetPromptResult>>? GetPromptHandler { get; init; } public Func<RequestContext<GetPromptRequestParams>, CancellationToken, Task<GetPromptResult>>? GetPromptHandler { get; set; }
} }
/// <summary> /// <summary>
/// Represents the resources capability configuration. /// Represents the resources capability configuration.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see> /// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary> /// </summary>
public record ResourcesCapability public class ResourcesCapability
{ {
/// <summary> /// <summary>
/// Whether this server supports subscribing to resource updates. /// Whether this server supports subscribing to resource updates.
/// </summary> /// </summary>
[JsonPropertyName("subscribe")] [JsonPropertyName("subscribe")]
public bool? Subscribe { get; init; } public bool? Subscribe { get; set; }
/// <summary> /// <summary>
/// Whether this server supports notifications for changes to the resource list. /// Whether this server supports notifications for changes to the resource list.
/// </summary> /// </summary>
[JsonPropertyName("listChanged")] [JsonPropertyName("listChanged")]
public bool? ListChanged { get; init; } public bool? ListChanged { get; set; }
/// <summary> /// <summary>
/// Gets or sets the handler for list resource templates requests. /// Gets or sets the handler for list resource templates requests.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public Func<RequestContext<ListResourceTemplatesRequestParams>, CancellationToken, Task<ListResourceTemplatesResult>>? ListResourceTemplatesHandler { get; init; } public Func<RequestContext<ListResourceTemplatesRequestParams>, CancellationToken, Task<ListResourceTemplatesResult>>? ListResourceTemplatesHandler { get; set; }
/// <summary> /// <summary>
/// Gets or sets the handler for list resources requests. /// Gets or sets the handler for list resources requests.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public Func<RequestContext<ListResourcesRequestParams>, CancellationToken, Task<ListResourcesResult>>? ListResourcesHandler { get; init; } public Func<RequestContext<ListResourcesRequestParams>, CancellationToken, Task<ListResourcesResult>>? ListResourcesHandler { get; set; }
/// <summary> /// <summary>
/// Gets or sets the handler for read resources requests. /// Gets or sets the handler for read resources requests.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public Func<RequestContext<ReadResourceRequestParams>, CancellationToken, Task<ReadResourceResult>>? ReadResourceHandler { get; init; } public Func<RequestContext<ReadResourceRequestParams>, CancellationToken, Task<ReadResourceResult>>? ReadResourceHandler { get; set; }
/// <summary> /// <summary>
/// Gets or sets the handler for subscribe to resources messages. /// Gets or sets the handler for subscribe to resources messages.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public Func<RequestContext<SubscribeRequestParams>, CancellationToken, Task<EmptyResult>>? SubscribeToResourcesHandler { get; init; } public Func<RequestContext<SubscribeRequestParams>, CancellationToken, Task<EmptyResult>>? SubscribeToResourcesHandler { get; set; }
/// <summary> /// <summary>
/// Gets or sets the handler for unsubscribe from resources messages. /// Gets or sets the handler for unsubscribe from resources messages.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public Func<RequestContext<UnsubscribeRequestParams>, CancellationToken, Task<EmptyResult>>? UnsubscribeFromResourcesHandler { get; init; } public Func<RequestContext<UnsubscribeRequestParams>, CancellationToken, Task<EmptyResult>>? UnsubscribeFromResourcesHandler { get; set; }
} }
/// <summary> /// <summary>
/// Represents the tools capability configuration. /// Represents the tools capability configuration.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see> /// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary> /// </summary>
public record ToolsCapability public class ToolsCapability
{ {
/// <summary> /// <summary>
/// Gets or sets whether this server supports notifications for changes to the tool list. /// Gets or sets whether this server supports notifications for changes to the tool list.
/// </summary> /// </summary>
[JsonPropertyName("listChanged")] [JsonPropertyName("listChanged")]
public bool? ListChanged { get; init; } public bool? ListChanged { get; set; }
/// <summary> /// <summary>
/// Gets or sets the handler for list tools requests. /// Gets or sets the handler for list tools requests.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public Func<RequestContext<ListToolsRequestParams>, CancellationToken, Task<ListToolsResult>>? ListToolsHandler { get; init; } public Func<RequestContext<ListToolsRequestParams>, CancellationToken, Task<ListToolsResult>>? ListToolsHandler { get; set; }
/// <summary> /// <summary>
/// Gets or sets the handler for call tool requests. /// Gets or sets the handler for call tool requests.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public Func<RequestContext<CallToolRequestParams>, CancellationToken, Task<CallToolResponse>>? CallToolHandler { get; init; } public Func<RequestContext<CallToolRequestParams>, CancellationToken, Task<CallToolResponse>>? CallToolHandler { get; set; }
/// <summary>Gets or sets a collection of tools served by the server.</summary> /// <summary>Gets or sets a collection of tools served by the server.</summary>
/// <remarks> /// <remarks>
@ -182,5 +182,5 @@ public record ToolsCapability
/// will be invoked as a fallback. /// will be invoked as a fallback.
/// </remarks> /// </remarks>
[JsonIgnore] [JsonIgnore]
public McpServerToolCollection? ToolCollection { get; init; } public McpServerToolCollection? ToolCollection { get; set; }
} }

View file

@ -6,17 +6,17 @@ namespace ModelContextProtocol.Protocol.Types;
/// Describes the name and version of an MCP implementation. /// Describes the name and version of an MCP implementation.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see> /// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary> /// </summary>
public record Implementation public class Implementation
{ {
/// <summary> /// <summary>
/// Name of the implementation. /// Name of the implementation.
/// </summary> /// </summary>
[JsonPropertyName("name")] [JsonPropertyName("name")]
public required string Name { get; init; } public required string Name { get; set; }
/// <summary> /// <summary>
/// Version of the implementation. /// Version of the implementation.
/// </summary> /// </summary>
[JsonPropertyName("version")] [JsonPropertyName("version")]
public required string Version { get; init; } public required string Version { get; set; }
} }

View file

@ -6,35 +6,35 @@ namespace ModelContextProtocol.Protocol.Types;
/// Represents the capabilities that a server may support. /// Represents the capabilities that a server may support.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see> /// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary> /// </summary>
public record ServerCapabilities public class ServerCapabilities
{ {
/// <summary> /// <summary>
/// Experimental, non-standard capabilities that the server supports. /// Experimental, non-standard capabilities that the server supports.
/// </summary> /// </summary>
[JsonPropertyName("experimental")] [JsonPropertyName("experimental")]
public Dictionary<string, object>? Experimental { get; init; } public Dictionary<string, object>? Experimental { get; set; }
/// <summary> /// <summary>
/// Present if the server supports sending log messages to the client. /// Present if the server supports sending log messages to the client.
/// </summary> /// </summary>
[JsonPropertyName("logging")] [JsonPropertyName("logging")]
public LoggingCapability? Logging { get; init; } public LoggingCapability? Logging { get; set; }
/// <summary> /// <summary>
/// Present if the server offers any prompt templates. /// Present if the server offers any prompt templates.
/// </summary> /// </summary>
[JsonPropertyName("prompts")] [JsonPropertyName("prompts")]
public PromptsCapability? Prompts { get; init; } public PromptsCapability? Prompts { get; set; }
/// <summary> /// <summary>
/// Present if the server offers any resources to read. /// Present if the server offers any resources to read.
/// </summary> /// </summary>
[JsonPropertyName("resources")] [JsonPropertyName("resources")]
public ResourcesCapability? Resources { get; init; } public ResourcesCapability? Resources { get; set; }
/// <summary> /// <summary>
/// Present if the server offers any tools to call. /// Present if the server offers any tools to call.
/// </summary> /// </summary>
[JsonPropertyName("tools")] [JsonPropertyName("tools")]
public ToolsCapability? Tools { get; init; } public ToolsCapability? Tools { get; set; }
} }

View file

@ -263,25 +263,14 @@ internal sealed class McpServer : McpJsonRpcEndpoint, IMcpServer
return tool.InvokeAsync(request, cancellationToken); return tool.InvokeAsync(request, cancellationToken);
}; };
toolsCapability = toolsCapability is null ? toolsCapability ??= new();
new() toolsCapability.CallToolHandler = callToolHandler;
{ toolsCapability.ListToolsHandler = listToolsHandler;
CallToolHandler = callToolHandler, toolsCapability.ToolCollection = tools;
ListToolsHandler = listToolsHandler, toolsCapability.ListChanged = true;
ToolCollection = tools,
ListChanged = true,
} :
toolsCapability with
{
CallToolHandler = callToolHandler,
ListToolsHandler = listToolsHandler,
ToolCollection = tools,
ListChanged = true,
};
options.Capabilities = options.Capabilities is null ? options.Capabilities ??= new();
new() { Tools = toolsCapability } : options.Capabilities.Tools = toolsCapability;
options.Capabilities with { Tools = toolsCapability };
tools.Changed += delegate tools.Changed += delegate
{ {

View file

@ -72,91 +72,47 @@ public sealed class McpServerHandlers
PromptsCapability? promptsCapability = options.Capabilities?.Prompts; PromptsCapability? promptsCapability = options.Capabilities?.Prompts;
if (ListPromptsHandler is not null || GetPromptHandler is not null) if (ListPromptsHandler is not null || GetPromptHandler is not null)
{ {
promptsCapability = promptsCapability is null ? promptsCapability ??= new();
new() promptsCapability.ListPromptsHandler = ListPromptsHandler ?? promptsCapability.ListPromptsHandler;
{ promptsCapability.GetPromptHandler = GetPromptHandler ?? promptsCapability.GetPromptHandler;
ListPromptsHandler = ListPromptsHandler,
GetPromptHandler = GetPromptHandler,
} :
promptsCapability with
{
ListPromptsHandler = ListPromptsHandler ?? promptsCapability.ListPromptsHandler,
GetPromptHandler = GetPromptHandler ?? promptsCapability.GetPromptHandler,
};
} }
ResourcesCapability? resourcesCapability = options.Capabilities?.Resources; ResourcesCapability? resourcesCapability = options.Capabilities?.Resources;
if (ListResourcesHandler is not null || if (ListResourcesHandler is not null ||
ReadResourceHandler is not null) ReadResourceHandler is not null)
{ {
resourcesCapability = resourcesCapability is null ? resourcesCapability ??= new();
new() resourcesCapability.ListResourceTemplatesHandler = ListResourceTemplatesHandler ?? resourcesCapability.ListResourceTemplatesHandler;
{ resourcesCapability.ListResourcesHandler = ListResourcesHandler ?? resourcesCapability.ListResourcesHandler;
ListResourceTemplatesHandler = ListResourceTemplatesHandler, resourcesCapability.ReadResourceHandler = ReadResourceHandler ?? resourcesCapability.ReadResourceHandler;
ListResourcesHandler = ListResourcesHandler,
ReadResourceHandler = ReadResourceHandler
} :
resourcesCapability with
{
ListResourceTemplatesHandler = ListResourceTemplatesHandler ?? resourcesCapability.ListResourceTemplatesHandler,
ListResourcesHandler = ListResourcesHandler ?? resourcesCapability.ListResourcesHandler,
ReadResourceHandler = ReadResourceHandler ?? resourcesCapability.ReadResourceHandler
};
if (SubscribeToResourcesHandler is not null || UnsubscribeFromResourcesHandler is not null) if (SubscribeToResourcesHandler is not null || UnsubscribeFromResourcesHandler is not null)
{ {
resourcesCapability = resourcesCapability with resourcesCapability.SubscribeToResourcesHandler = SubscribeToResourcesHandler ?? resourcesCapability.SubscribeToResourcesHandler;
{ resourcesCapability.UnsubscribeFromResourcesHandler = UnsubscribeFromResourcesHandler ?? resourcesCapability.UnsubscribeFromResourcesHandler;
SubscribeToResourcesHandler = SubscribeToResourcesHandler ?? resourcesCapability.SubscribeToResourcesHandler, resourcesCapability.Subscribe = true;
UnsubscribeFromResourcesHandler = UnsubscribeFromResourcesHandler ?? resourcesCapability.UnsubscribeFromResourcesHandler,
Subscribe = true
};
} }
} }
ToolsCapability? toolsCapability = options.Capabilities?.Tools; ToolsCapability? toolsCapability = options.Capabilities?.Tools;
if (ListToolsHandler is not null || CallToolHandler is not null) if (ListToolsHandler is not null || CallToolHandler is not null)
{ {
toolsCapability = toolsCapability is null ? toolsCapability ??= new();
new() toolsCapability.ListToolsHandler = ListToolsHandler ?? toolsCapability.ListToolsHandler;
{ toolsCapability.CallToolHandler = CallToolHandler ?? toolsCapability.CallToolHandler;
ListToolsHandler = ListToolsHandler,
CallToolHandler = CallToolHandler,
} :
toolsCapability with
{
ListToolsHandler = ListToolsHandler ?? toolsCapability.ListToolsHandler,
CallToolHandler = CallToolHandler ?? toolsCapability.CallToolHandler,
};
} }
LoggingCapability? loggingCapability = options.Capabilities?.Logging; LoggingCapability? loggingCapability = options.Capabilities?.Logging;
if (SetLoggingLevelHandler is not null) if (SetLoggingLevelHandler is not null)
{ {
loggingCapability = loggingCapability is null ? loggingCapability ??= new();
new() loggingCapability.SetLoggingLevelHandler = SetLoggingLevelHandler;
{
SetLoggingLevelHandler = SetLoggingLevelHandler,
} :
loggingCapability with
{
SetLoggingLevelHandler = SetLoggingLevelHandler ?? loggingCapability.SetLoggingLevelHandler,
};
} }
options.Capabilities = options.Capabilities is null ? options.Capabilities ??= new();
new() options.Capabilities.Prompts = promptsCapability;
{ options.Capabilities.Resources = resourcesCapability;
Prompts = promptsCapability, options.Capabilities.Tools = toolsCapability;
Resources = resourcesCapability,
Tools = toolsCapability,
} :
options.Capabilities with
{
Prompts = promptsCapability,
Resources = resourcesCapability,
Tools = toolsCapability,
};
options.GetCompletionHandler = GetCompletionHandler ?? options.GetCompletionHandler; options.GetCompletionHandler = GetCompletionHandler ?? options.GetCompletionHandler;
} }

View file

@ -15,7 +15,6 @@ public class SseServerIntegrationTestFixture : IAsyncDisposable
private readonly DelegatingTestOutputHelper _delegatingTestOutputHelper = new(); private readonly DelegatingTestOutputHelper _delegatingTestOutputHelper = new();
private readonly ILoggerFactory _redirectingLoggerFactory; private readonly ILoggerFactory _redirectingLoggerFactory;
public McpClientOptions DefaultOptions { get; }
public McpServerConfig DefaultConfig { get; } public McpServerConfig DefaultConfig { get; }
public SseServerIntegrationTestFixture() public SseServerIntegrationTestFixture()
@ -26,11 +25,6 @@ public class SseServerIntegrationTestFixture : IAsyncDisposable
builder.AddProvider(new XunitLoggerProvider(_delegatingTestOutputHelper)); builder.AddProvider(new XunitLoggerProvider(_delegatingTestOutputHelper));
}); });
DefaultOptions = new()
{
ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" },
};
DefaultConfig = new McpServerConfig DefaultConfig = new McpServerConfig
{ {
Id = "test_server", Id = "test_server",
@ -43,6 +37,11 @@ public class SseServerIntegrationTestFixture : IAsyncDisposable
_serverTask = Program.MainAsync([], _redirectingLoggerFactory, _stopCts.Token); _serverTask = Program.MainAsync([], _redirectingLoggerFactory, _stopCts.Token);
} }
public static McpClientOptions CreateDefaultClientOptions() => new()
{
ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" },
};
public void Initialize(ITestOutputHelper output) public void Initialize(ITestOutputHelper output)
{ {
_delegatingTestOutputHelper.CurrentTestOutputHelper = output; _delegatingTestOutputHelper.CurrentTestOutputHelper = output;

View file

@ -19,7 +19,7 @@ public class SseServerIntegrationTests : LoggedTest, IClassFixture<SseServerInte
{ {
return McpClientFactory.CreateAsync( return McpClientFactory.CreateAsync(
_fixture.DefaultConfig, _fixture.DefaultConfig,
options ?? _fixture.DefaultOptions, options ?? SseServerIntegrationTestFixture.CreateDefaultClientOptions(),
loggerFactory: LoggerFactory); loggerFactory: LoggerFactory);
} }
@ -201,28 +201,22 @@ public class SseServerIntegrationTests : LoggedTest, IClassFixture<SseServerInte
// Set up the sampling handler // Set up the sampling handler
int samplingHandlerCalls = 0; int samplingHandlerCalls = 0;
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously #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 (_, _) => Type = "text",
{ Text = "Test response"
samplingHandlerCalls++;
return new CreateMessageResult
{
Model = "test-model",
Role = "assistant",
Content = new Content
{
Type = "text",
Text = "Test response"
}
};
}
} }
} };
}; };
var client = await GetClientAsync(options); var client = await GetClientAsync(options);
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously