using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ModelContextProtocol.Hosting;
using ModelContextProtocol.Protocol.Transport;
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Server;
using ModelContextProtocol.Utils;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json;
namespace Microsoft.Extensions.DependencyInjection;
///
/// Provides methods for configuring MCP servers via dependency injection.
///
public static partial class McpServerBuilderExtensions
{
#region WithTools
private const string WithToolsRequiresUnreferencedCodeMessage =
$"The non-generic {nameof(WithTools)} and {nameof(WithToolsFromAssembly)} methods require dynamic lookup of method metadata" +
$"and may not work in Native AOT. Use the generic {nameof(WithTools)} method instead.";
/// Adds instances to the service collection backing .
/// The tool type.
/// The builder instance.
/// The serializer options governing tool parameter marshalling.
/// The builder provided in .
/// is .
///
/// This method discovers all instance and static methods (public and non-public) on the specified
/// type, where the methods are attributed as , and adds an
/// instance for each. For instance methods, an instance will be constructed for each invocation of the tool.
///
public static IMcpServerBuilder WithTools<[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods |
DynamicallyAccessedMemberTypes.PublicConstructors)] TToolType>(
this IMcpServerBuilder builder,
JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);
foreach (var toolMethod in typeof(TToolType).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
if (toolMethod.GetCustomAttribute() is not null)
{
builder.Services.AddSingleton((Func)(toolMethod.IsStatic ?
services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions }) :
services => McpServerTool.Create(toolMethod, typeof(TToolType), new() { Services = services, SerializerOptions = serializerOptions })));
}
}
return builder;
}
/// Adds instances to the service collection backing .
/// The builder instance.
/// The instances to add to the server.
/// The builder provided in .
/// is .
/// is .
public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnumerable tools)
{
Throw.IfNull(builder);
Throw.IfNull(tools);
foreach (var tool in tools)
{
if (tool is not null)
{
builder.Services.AddSingleton(tool);
}
}
return builder;
}
/// Adds instances to the service collection backing .
/// The builder instance.
/// Types with -attributed methods to add as tools to the server.
/// The serializer options governing tool parameter marshalling.
/// The builder provided in .
/// is .
/// is .
///
/// This method discovers all instance and static methods (public and non-public) on the specified
/// types, where the methods are attributed as , and adds an
/// instance for each. For instance methods, an instance will be constructed for each invocation of the tool.
///
[RequiresUnreferencedCode(WithToolsRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnumerable toolTypes, JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);
Throw.IfNull(toolTypes);
foreach (var toolType in toolTypes)
{
if (toolType is not null)
{
foreach (var toolMethod in toolType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
if (toolMethod.GetCustomAttribute() is not null)
{
builder.Services.AddSingleton((Func)(toolMethod.IsStatic ?
services => McpServerTool.Create(toolMethod, options: new() { Services = services , SerializerOptions = serializerOptions }) :
services => McpServerTool.Create(toolMethod, toolType, new() { Services = services , SerializerOptions = serializerOptions })));
}
}
}
}
return builder;
}
///
/// Adds types marked with the attribute from the given assembly as tools to the server.
///
/// The builder instance.
/// The serializer options governing tool parameter marshalling.
/// The assembly to load the types from. If , the calling assembly will be used.
/// The builder provided in .
/// is .
///
///
/// This method scans the specified assembly (or the calling assembly if none is provided) for classes
/// marked with the . It then discovers all methods within those
/// classes that are marked with the and registers them as s
/// in the 's .
///
///
/// The method automatically handles both static and instance methods. For instance methods, a new instance
/// of the containing class will be constructed for each invocation of the tool.
///
///
/// Tools registered through this method can be discovered by clients using the list_tools request
/// and invoked using the call_tool request.
///
///
/// Note that this method performs reflection at runtime and may not work in Native AOT scenarios. For
/// Native AOT compatibility, consider using the generic method instead.
///
///
[RequiresUnreferencedCode(WithToolsRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithToolsFromAssembly(this IMcpServerBuilder builder, Assembly? toolAssembly = null, JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);
toolAssembly ??= Assembly.GetCallingAssembly();
return builder.WithTools(
from t in toolAssembly.GetTypes()
where t.GetCustomAttribute() is not null
select t,
serializerOptions);
}
#endregion
#region WithPrompts
private const string WithPromptsRequiresUnreferencedCodeMessage =
$"The non-generic {nameof(WithPrompts)} and {nameof(WithPromptsFromAssembly)} methods require dynamic lookup of method metadata" +
$"and may not work in Native AOT. Use the generic {nameof(WithPrompts)} method instead.";
/// Adds instances to the service collection backing .
/// The prompt type.
/// The builder instance.
/// The serializer options governing prompt parameter marshalling.
/// The builder provided in .
/// is .
///
/// This method discovers all instance and static methods (public and non-public) on the specified
/// type, where the methods are attributed as , and adds an
/// instance for each. For instance methods, an instance will be constructed for each invocation of the prompt.
///
public static IMcpServerBuilder WithPrompts<[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods |
DynamicallyAccessedMemberTypes.PublicConstructors)] TPromptType>(
this IMcpServerBuilder builder,
JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);
foreach (var promptMethod in typeof(TPromptType).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
if (promptMethod.GetCustomAttribute() is not null)
{
builder.Services.AddSingleton((Func)(promptMethod.IsStatic ?
services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions }) :
services => McpServerPrompt.Create(promptMethod, typeof(TPromptType), new() { Services = services, SerializerOptions = serializerOptions })));
}
}
return builder;
}
/// Adds instances to the service collection backing .
/// The builder instance.
/// The instances to add to the server.
/// The builder provided in .
/// is .
/// is .
public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnumerable prompts)
{
Throw.IfNull(builder);
Throw.IfNull(prompts);
foreach (var prompt in prompts)
{
if (prompt is not null)
{
builder.Services.AddSingleton(prompt);
}
}
return builder;
}
/// Adds instances to the service collection backing .
/// The builder instance.
/// Types with marked methods to add as prompts to the server.
/// The serializer options governing prompt parameter marshalling.
/// The builder provided in .
/// is .
/// is .
///
/// This method discovers all instance and static methods (public and non-public) on the specified
/// types, where the methods are attributed as , and adds an
/// instance for each. For instance methods, an instance will be constructed for each invocation of the prompt.
///
[RequiresUnreferencedCode(WithPromptsRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnumerable promptTypes, JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);
Throw.IfNull(promptTypes);
foreach (var promptType in promptTypes)
{
if (promptType is not null)
{
foreach (var promptMethod in promptType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
if (promptMethod.GetCustomAttribute() is not null)
{
builder.Services.AddSingleton((Func)(promptMethod.IsStatic ?
services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions }) :
services => McpServerPrompt.Create(promptMethod, promptType, new() { Services = services, SerializerOptions = serializerOptions })));
}
}
}
}
return builder;
}
///
/// Adds types marked with the attribute from the given assembly as prompts to the server.
///
/// The builder instance.
/// The serializer options governing prompt parameter marshalling.
/// The assembly to load the types from. If , the calling assembly will be used.
/// The builder provided in .
/// is .
///
///
/// This method scans the specified assembly (or the calling assembly if none is provided) for classes
/// marked with the . It then discovers all methods within those
/// classes that are marked with the and registers them as s
/// in the 's .
///
///
/// The method automatically handles both static and instance methods. For instance methods, a new instance
/// of the containing class will be constructed for each invocation of the prompt.
///
///
/// Prompts registered through this method can be discovered by clients using the list_prompts request
/// and invoked using the call_prompt request.
///
///
/// Note that this method performs reflection at runtime and may not work in Native AOT scenarios. For
/// Native AOT compatibility, consider using the generic method instead.
///
///
[RequiresUnreferencedCode(WithPromptsRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithPromptsFromAssembly(this IMcpServerBuilder builder, Assembly? promptAssembly = null, JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);
promptAssembly ??= Assembly.GetCallingAssembly();
return builder.WithPrompts(
from t in promptAssembly.GetTypes()
where t.GetCustomAttribute() is not null
select t,
serializerOptions);
}
#endregion
#region WithResources
private const string WithResourcesRequiresUnreferencedCodeMessage =
$"The non-generic {nameof(WithResources)} and {nameof(WithResourcesFromAssembly)} methods require dynamic lookup of member metadata" +
$"and may not work in Native AOT. Use the generic {nameof(WithResources)} method instead.";
/// Adds instances to the service collection backing .
/// The resource type.
/// The builder instance.
/// The builder provided in .
/// is .
///
/// This method discovers all instance and static methods (public and non-public) on the specified
/// type, where the members are attributed as , and adds an
/// instance for each. For instance members, an instance will be constructed for each invocation of the resource.
///
public static IMcpServerBuilder WithResources<[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods |
DynamicallyAccessedMemberTypes.PublicConstructors)] TResourceType>(
this IMcpServerBuilder builder)
{
Throw.IfNull(builder);
foreach (var resourceTemplateMethod in typeof(TResourceType).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
if (resourceTemplateMethod.GetCustomAttribute() is not null)
{
builder.Services.AddSingleton((Func)(resourceTemplateMethod.IsStatic ?
services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services }) :
services => McpServerResource.Create(resourceTemplateMethod, typeof(TResourceType), new() { Services = services })));
}
}
return builder;
}
/// Adds instances to the service collection backing .
/// The builder instance.
/// The instances to add to the server.
/// The builder provided in .
/// is .
/// is .
public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable resourcetemplates)
{
Throw.IfNull(builder);
Throw.IfNull(resourcetemplates);
foreach (var resourceTemplate in resourcetemplates)
{
if (resourceTemplate is not null)
{
builder.Services.AddSingleton(resourceTemplate);
}
}
return builder;
}
/// Adds instances to the service collection backing .
/// The builder instance.
/// Types with marked methods to add as resources to the server.
/// The builder provided in .
/// is .
/// is .
///
/// This method discovers all instance and static methods (public and non-public) on the specified
/// types, where the methods are attributed as , and adds an
/// instance for each. For instance methods, an instance will be constructed for each invocation of the resource.
///
[RequiresUnreferencedCode(WithResourcesRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable resourceTemplateTypes)
{
Throw.IfNull(builder);
Throw.IfNull(resourceTemplateTypes);
foreach (var resourceTemplateType in resourceTemplateTypes)
{
if (resourceTemplateType is not null)
{
foreach (var resourceTemplateMethod in resourceTemplateType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
if (resourceTemplateMethod.GetCustomAttribute() is not null)
{
builder.Services.AddSingleton((Func)(resourceTemplateMethod.IsStatic ?
services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services }) :
services => McpServerResource.Create(resourceTemplateMethod, resourceTemplateType, new() { Services = services })));
}
}
}
}
return builder;
}
///
/// Adds types marked with the attribute from the given assembly as resources to the server.
///
/// The builder instance.
/// The assembly to load the types from. If , the calling assembly will be used.
/// The builder provided in .
/// is .
///
///
/// This method scans the specified assembly (or the calling assembly if none is provided) for classes
/// marked with the . It then discovers all members within those
/// classes that are marked with the and registers them as s
/// in the 's .
///
///
/// The method automatically handles both static and instance members. For instance members, a new instance
/// of the containing class will be constructed for each invocation of the resource.
///
///
/// Resource templates registered through this method can be discovered by clients using the list_resourcetemplates request
/// and invoked using the read_resource request.
///
///
/// Note that this method performs reflection at runtime and may not work in Native AOT scenarios. For
/// Native AOT compatibility, consider using the generic method instead.
///
///
[RequiresUnreferencedCode(WithResourcesRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithResourcesFromAssembly(this IMcpServerBuilder builder, Assembly? resourceAssembly = null)
{
Throw.IfNull(builder);
resourceAssembly ??= Assembly.GetCallingAssembly();
return builder.WithResources(
from t in resourceAssembly.GetTypes()
where t.GetCustomAttribute() is not null
select t);
}
#endregion
#region Handlers
///
/// Configures a handler for listing resource templates available from the Model Context Protocol server.
///
/// The builder instance.
/// The handler function that processes resource template list requests.
/// The builder provided in .
/// is .
///
///
/// This handler is responsible for providing clients with information about available resource templates
/// that can be used to construct resource URIs.
///
///
/// Resource templates describe the structure of resource URIs that the server can handle. They include
/// URI templates (according to RFC 6570) that clients can use to construct valid resource URIs.
///
///
/// This handler is typically paired with to provide a complete
/// resource system where templates define the URI patterns and the read handler provides the actual content.
///
///
public static IMcpServerBuilder WithListResourceTemplatesHandler(this IMcpServerBuilder builder, Func, CancellationToken, ValueTask> handler)
{
Throw.IfNull(builder);
builder.Services.Configure(s => s.ListResourceTemplatesHandler = handler);
return builder;
}
///
/// Configures a handler for listing tools available from the Model Context Protocol server.
///
/// The builder instance.
/// The handler that processes list tools requests.
/// The builder provided in .
/// is .
///
///
/// This handler is called when a client requests a list of available tools. It should return all tools
/// that can be invoked through the server, including their names, descriptions, and parameter specifications.
/// The handler can optionally support pagination via the cursor mechanism for large or dynamically-generated
/// tool collections.
///
///
/// When tools are also defined using collection, both sets of tools
/// will be combined in the response to clients. This allows for a mix of programmatically defined
/// tools and dynamically generated tools.
///
///
/// This method is typically paired with to provide a complete tools implementation,
/// where advertises available tools and
/// executes them when invoked by clients.
///
///
public static IMcpServerBuilder WithListToolsHandler(this IMcpServerBuilder builder, Func, CancellationToken, ValueTask> handler)
{
Throw.IfNull(builder);
builder.Services.Configure(s => s.ListToolsHandler = handler);
return builder;
}
///
/// Configures a handler for calling tools available from the Model Context Protocol server.
///
/// The builder instance.
/// The handler function that processes tool calls.
/// The builder provided in .
/// is .
///
/// The call tool handler is responsible for executing custom tools and returning their results to clients.
/// This method is typically paired with to provide a complete tools implementation,
/// where advertises available tools and this handler executes them.
///
public static IMcpServerBuilder WithCallToolHandler(this IMcpServerBuilder builder, Func, CancellationToken, ValueTask> handler)
{
Throw.IfNull(builder);
builder.Services.Configure(s => s.CallToolHandler = handler);
return builder;
}
///
/// Configures a handler for listing prompts available from the Model Context Protocol server.
///
/// The builder instance.
/// The handler that processes list prompts requests.
/// The builder provided in .
/// is .
///
///
/// This handler is called when a client requests a list of available prompts. It should return all prompts
/// that can be invoked through the server, including their names, descriptions, and parameter specifications.
/// The handler can optionally support pagination via the cursor mechanism for large or dynamically-generated
/// prompt collections.
///
///
/// When prompts are also defined using collection, both sets of prompts
/// will be combined in the response to clients. This allows for a mix of programmatically defined
/// prompts and dynamically generated prompts.
///
///
/// This method is typically paired with to provide a complete prompts implementation,
/// where advertises available prompts and
/// produces them when invoked by clients.
///
///
public static IMcpServerBuilder WithListPromptsHandler(this IMcpServerBuilder builder, Func, CancellationToken, ValueTask> handler)
{
Throw.IfNull(builder);
builder.Services.Configure(s => s.ListPromptsHandler = handler);
return builder;
}
///
/// Configures a handler for getting a prompt available from the Model Context Protocol server.
///
/// The builder instance.
/// The handler function that processes prompt requests.
/// The builder provided in .
/// is .
public static IMcpServerBuilder WithGetPromptHandler(this IMcpServerBuilder builder, Func, CancellationToken, ValueTask> handler)
{
Throw.IfNull(builder);
builder.Services.Configure(s => s.GetPromptHandler = handler);
return builder;
}
///
/// Configures a handler for listing resources available from the Model Context Protocol server.
///
/// The builder instance.
/// The handler function that processes resource list requests.
/// The builder provided in .
/// is .
///
///
/// This handler is typically paired with to provide a complete resources implementation,
/// where this handler advertises available resources and the read handler provides their content when requested.
///
///
public static IMcpServerBuilder WithListResourcesHandler(this IMcpServerBuilder builder, Func, CancellationToken, ValueTask> handler)
{
Throw.IfNull(builder);
builder.Services.Configure(s => s.ListResourcesHandler = handler);
return builder;
}
///
/// Configures a handler for reading a resource available from the Model Context Protocol server.
///
/// The builder instance.
/// The handler function that processes resource read requests.
/// The builder provided in .
/// is .
///
/// This handler is typically paired with to provide a complete resources implementation,
/// where the list handler advertises available resources and the read handler provides their content when requested.
///
public static IMcpServerBuilder WithReadResourceHandler(this IMcpServerBuilder builder, Func, CancellationToken, ValueTask> handler)
{
Throw.IfNull(builder);
builder.Services.Configure(s => s.ReadResourceHandler = handler);
return builder;
}
///
/// Configures a handler for auto-completion suggestions for prompt arguments or resource references available from the Model Context Protocol server.
///
/// The builder instance.
/// The handler function that processes completion requests.
/// The builder provided in .
/// is .
///
/// The completion handler is invoked when clients request suggestions for argument values.
/// This enables auto-complete functionality for both prompt arguments and resource references.
///
public static IMcpServerBuilder WithCompleteHandler(this IMcpServerBuilder builder, Func, CancellationToken, ValueTask> handler)
{
Throw.IfNull(builder);
builder.Services.Configure(s => s.CompleteHandler = handler);
return builder;
}
///
/// Configures a handler for resource subscription requests.
///
/// The builder instance.
/// The handler function that processes resource subscription requests.
/// The builder provided in .
/// is .
///
///
/// The subscribe handler is responsible for registering client interest in specific resources. When a resource
/// changes, the server can notify all subscribed clients about the change.
///
///
/// This handler is typically paired with to provide a complete
/// subscription management system. Resource subscriptions allow clients to maintain up-to-date information without
/// needing to poll resources constantly.
///
///
/// After registering a subscription, it's the server's responsibility to track which client is subscribed to which
/// resources and to send appropriate notifications through the connection when resources change.
///
///
public static IMcpServerBuilder WithSubscribeToResourcesHandler(this IMcpServerBuilder builder, Func, CancellationToken, ValueTask> handler)
{
Throw.IfNull(builder);
builder.Services.Configure(s => s.SubscribeToResourcesHandler = handler);
return builder;
}
///
/// Configures a handler for resource unsubscription requests.
///
/// The builder instance.
/// The handler function that processes resource unsubscription requests.
/// The builder provided in .
/// is .
///
///
/// The unsubscribe handler is responsible for removing client interest in specific resources. When a client
/// no longer needs to receive notifications about resource changes, it can send an unsubscribe request.
///
///
/// This handler is typically paired with to provide a complete
/// subscription management system. The unsubscribe operation is idempotent, meaning it can be called multiple
/// times for the same resource without causing errors, even if there is no active subscription.
///
///
/// After removing a subscription, the server should stop sending notifications to the client about changes
/// to the specified resource.
///
///
public static IMcpServerBuilder WithUnsubscribeFromResourcesHandler(this IMcpServerBuilder builder, Func, CancellationToken, ValueTask> handler)
{
Throw.IfNull(builder);
builder.Services.Configure(s => s.UnsubscribeFromResourcesHandler = handler);
return builder;
}
///
/// Configures a handler for processing logging level change requests from clients.
///
/// The server builder instance.
/// The handler that processes requests to change the logging level.
/// The builder provided in .
/// is .
///
///
/// When a client sends a logging/setLevel request, this handler will be invoked to process
/// the requested level change. The server typically adjusts its internal logging level threshold
/// and may begin sending log messages at or above the specified level to the client.
///
///
/// Regardless of whether a handler is provided, an should itself handle
/// such notifications by updating its property to return the
/// most recently set level.
///
///
public static IMcpServerBuilder WithSetLoggingLevelHandler(this IMcpServerBuilder builder, Func, CancellationToken, ValueTask> handler)
{
Throw.IfNull(builder);
builder.Services.Configure(s => s.SetLoggingLevelHandler = handler);
return builder;
}
#endregion
#region Transports
///
/// Adds a server transport that uses standard input (stdin) and standard output (stdout) for communication.
///
/// The builder instance.
/// The builder provided in .
/// is .
///
///
/// This method configures the server to communicate using the standard input and output streams,
/// which is commonly used when the Model Context Protocol server is launched locally by a client process.
///
///
/// When using this transport, the server runs as a single-session service that exits when the
/// stdin stream is closed. This makes it suitable for scenarios where the server should terminate
/// when the parent process disconnects.
///
///
/// is .
public static IMcpServerBuilder WithStdioServerTransport(this IMcpServerBuilder builder)
{
Throw.IfNull(builder);
AddSingleSessionServerDependencies(builder.Services);
builder.Services.AddSingleton();
return builder;
}
///
/// Adds a server transport that uses the specified input and output streams for communication.
///
/// The builder instance.
/// The input to use as standard input.
/// The output to use as standard output.
/// The builder provided in .
/// is .
/// is .
/// is .
public static IMcpServerBuilder WithStreamServerTransport(
this IMcpServerBuilder builder,
Stream inputStream,
Stream outputStream)
{
Throw.IfNull(builder);
Throw.IfNull(inputStream);
Throw.IfNull(outputStream);
AddSingleSessionServerDependencies(builder.Services);
builder.Services.AddSingleton(new StreamServerTransport(inputStream, outputStream));
return builder;
}
private static void AddSingleSessionServerDependencies(IServiceCollection services)
{
services.AddHostedService();
services.TryAddSingleton(services =>
{
ITransport serverTransport = services.GetRequiredService();
IOptions options = services.GetRequiredService>();
ILoggerFactory? loggerFactory = services.GetService();
return McpServerFactory.Create(serverTransport, options.Value, loggerFactory, services);
});
}
#endregion
}