diff --git a/mcpdotnet.sln b/mcpdotnet.sln
index 375a103..5781724 100644
--- a/mcpdotnet.sln
+++ b/mcpdotnet.sln
@@ -17,6 +17,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "McpDotNet.Extensions.AI", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "McpDotNet.Extensions.AI.Tests", "tests\McpDotNet.Extensions.AI.Tests\McpDotNet.Extensions.AI.Tests.csproj", "{67BEF596-AD39-4BDD-B505-31DE7FF0C8D2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestServerWithHosting", "samples\TestServerWithHosting\TestServerWithHosting.csproj", "{6499876E-2F76-44A8-B6EB-5B889C6E9B7F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -51,10 +55,19 @@ Global
{67BEF596-AD39-4BDD-B505-31DE7FF0C8D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67BEF596-AD39-4BDD-B505-31DE7FF0C8D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67BEF596-AD39-4BDD-B505-31DE7FF0C8D2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6499876E-2F76-44A8-B6EB-5B889C6E9B7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6499876E-2F76-44A8-B6EB-5B889C6E9B7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6499876E-2F76-44A8-B6EB-5B889C6E9B7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6499876E-2F76-44A8-B6EB-5B889C6E9B7F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {76E295FA-9E85-7B99-332A-2CDBFCD5860A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {CA0BB450-1903-2F92-A68D-1285976551D6} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {6499876E-2F76-44A8-B6EB-5B889C6E9B7F} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {384A3888-751F-4D75-9AE5-587330582D89}
EndGlobalSection
diff --git a/samples/TestServerWithHosting/Program.cs b/samples/TestServerWithHosting/Program.cs
new file mode 100644
index 0000000..d0e3f81
--- /dev/null
+++ b/samples/TestServerWithHosting/Program.cs
@@ -0,0 +1,37 @@
+using McpDotNet;
+using Microsoft.Extensions.Hosting;
+using Serilog;
+
+Log.Logger = new LoggerConfiguration()
+ .MinimumLevel.Verbose() // Capture all log levels
+ .WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "TestServer_.log"),
+ rollingInterval: RollingInterval.Day,
+ outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
+ .WriteTo.Debug()
+ .WriteTo.Console()
+ .CreateLogger();
+
+try
+{
+ Log.Information("Starting server...");
+
+ var builder = Host.CreateApplicationBuilder(args);
+ builder.Services.AddSerilog();
+ builder.Services.AddMcpServer()
+ .WithStdioServerTransport()
+ .WithCallToolHandler(async (r, ct) => new McpDotNet.Protocol.Types.CallToolResponse());
+
+ var app = builder.Build();
+
+ await app.RunAsync();
+ return 0;
+}
+catch (Exception ex)
+{
+ Log.Fatal(ex, "Host terminated unexpectedly");
+ return 1;
+}
+finally
+{
+ await Log.CloseAndFlushAsync();
+}
\ No newline at end of file
diff --git a/samples/TestServerWithHosting/TestServerWithHosting.csproj b/samples/TestServerWithHosting/TestServerWithHosting.csproj
new file mode 100644
index 0000000..34fb993
--- /dev/null
+++ b/samples/TestServerWithHosting/TestServerWithHosting.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mcpdotnet/Configuration/DefaultMcpServerBuilder.cs b/src/mcpdotnet/Configuration/DefaultMcpServerBuilder.cs
new file mode 100644
index 0000000..5559d71
--- /dev/null
+++ b/src/mcpdotnet/Configuration/DefaultMcpServerBuilder.cs
@@ -0,0 +1,22 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace McpDotNet.Configuration;
+
+///
+/// Default implementation of .
+///
+internal class DefaultMcpServerBuilder : IMcpServerBuilder
+{
+ ///
+ public IServiceCollection Services { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ public DefaultMcpServerBuilder(IServiceCollection services)
+ {
+ Services = services ?? throw new ArgumentNullException(nameof(services));
+ }
+}
diff --git a/src/mcpdotnet/Configuration/IMcpServerBuilder.cs b/src/mcpdotnet/Configuration/IMcpServerBuilder.cs
new file mode 100644
index 0000000..163b039
--- /dev/null
+++ b/src/mcpdotnet/Configuration/IMcpServerBuilder.cs
@@ -0,0 +1,15 @@
+using McpDotNet.Server;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace McpDotNet.Configuration;
+
+///
+/// Builder for configuring instances.
+///
+public interface IMcpServerBuilder
+{
+ ///
+ /// Gets the service collection.
+ ///
+ IServiceCollection Services { get; }
+}
diff --git a/src/mcpdotnet/Configuration/McpServerBuilderExtensions.cs b/src/mcpdotnet/Configuration/McpServerBuilderExtensions.cs
new file mode 100644
index 0000000..acfb9ea
--- /dev/null
+++ b/src/mcpdotnet/Configuration/McpServerBuilderExtensions.cs
@@ -0,0 +1,132 @@
+using McpDotNet.Configuration;
+using McpDotNet.Protocol.Transport;
+using McpDotNet.Protocol.Types;
+using McpDotNet.Server;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace McpDotNet;
+
+///
+/// Extension to configure the MCP server
+///
+public static class McpServerBuilderExtensions
+{
+ ///
+ /// Adds a server transport that uses stdin/stdout for communication.
+ ///
+ /// The builder instance.
+ ///
+ public static IMcpServerBuilder WithStdioServerTransport(this IMcpServerBuilder builder)
+ {
+ builder.Services.AddSingleton();
+ return builder;
+ }
+
+ ///
+ /// Sets the handler for list tools requests.
+ ///
+ /// The builder instance.
+ /// The handler.
+ ///
+ public static IMcpServerBuilder WithListToolsHandler(this IMcpServerBuilder builder, Func> handler)
+ {
+ builder.Services.Configure(s => s.ListToolsHandler = handler);
+ return builder;
+ }
+
+ ///
+ /// Sets the handler for call tool requests.
+ ///
+ /// The builder instance.
+ /// The handler.
+ ///
+ public static IMcpServerBuilder WithCallToolHandler(this IMcpServerBuilder builder, Func> handler)
+ {
+ builder.Services.Configure(s => s.CallToolHandler = handler);
+ return builder;
+ }
+
+ ///
+ /// Sets the handler for list prompts requests.
+ ///
+ /// The builder instance.
+ /// The handler.
+ ///
+ public static IMcpServerBuilder WithListPromptsHandler(this IMcpServerBuilder builder, Func> handler)
+ {
+ builder.Services.Configure(s => s.ListPromptsHandler = handler);
+ return builder;
+ }
+
+ ///
+ /// Sets the handler for get prompt requests.
+ ///
+ /// The builder instance.
+ /// The handler.
+ ///
+ public static IMcpServerBuilder WithGetPromptHandler(this IMcpServerBuilder builder, Func> handler)
+ {
+ builder.Services.Configure(s => s.GetPromptHandler = handler);
+ return builder;
+ }
+
+ ///
+ /// Sets the handler for list resources requests.
+ ///
+ /// The builder instance.
+ /// The handler.
+ ///
+ public static IMcpServerBuilder WithListResourcesHandler(this IMcpServerBuilder builder, Func> handler)
+ {
+ builder.Services.Configure(s => s.ListResourcesHandler = handler);
+ return builder;
+ }
+
+ ///
+ /// Sets the handler for read resources requests.
+ ///
+ /// The builder instance.
+ /// The handler.
+ ///
+ public static IMcpServerBuilder WithReadResourceHandler(this IMcpServerBuilder builder, Func> handler)
+ {
+ builder.Services.Configure(s => s.ReadResourceHandler = handler);
+ return builder;
+ }
+
+ ///
+ /// Sets the handler for get resources requests.
+ ///
+ /// The builder instance.
+ /// The handler.
+ ///
+ public static IMcpServerBuilder WithGetCompletionHandler(this IMcpServerBuilder builder, Func> handler)
+ {
+ builder.Services.Configure(s => s.GetCompletionHandler = handler);
+ return builder;
+ }
+
+ ///
+ /// Sets the handler for subscribe to resources messages.
+ ///
+ /// The builder instance.
+ /// The handler.
+ ///
+ public static IMcpServerBuilder WithSubscribeToResourcesHandler(this IMcpServerBuilder builder, Func handler)
+ {
+ builder.Services.Configure(s => s.SubscribeToResourcesHandler = handler);
+ return builder;
+ }
+
+ ///
+ /// Sets or sets the handler for subscribe to resources messages.
+ ///
+ /// The builder instance.
+ /// The handler.
+ ///
+ public static IMcpServerBuilder WithUnsubscribeFromResourcesHandler(this IMcpServerBuilder builder, Func handler)
+ {
+ builder.Services.Configure(s => s.UnsubscribeFromResourcesHandler = handler);
+ return builder;
+ }
+}
diff --git a/src/mcpdotnet/Configuration/McpServerServiceCollectionExtension.cs b/src/mcpdotnet/Configuration/McpServerServiceCollectionExtension.cs
new file mode 100644
index 0000000..476efbd
--- /dev/null
+++ b/src/mcpdotnet/Configuration/McpServerServiceCollectionExtension.cs
@@ -0,0 +1,63 @@
+using System.Reflection;
+using McpDotNet.Configuration;
+using McpDotNet.Hosting;
+using McpDotNet.Protocol.Types;
+using McpDotNet.Server;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace McpDotNet;
+
+///
+/// Extension to host the MCP server
+///
+public static class McpServerServiceCollectionExtension
+{
+ ///
+ /// Adds the MCP server to the service collection with default options.
+ ///
+ ///
+ ///
+ ///
+ public static IMcpServerBuilder AddMcpServer(this IServiceCollection services, Action? configureOptions = null)
+ {
+ var options = CreateDefaultServerOptions();
+ configureOptions?.Invoke(options);
+
+ return AddMcpServer(services, options);
+ }
+
+ ///
+ /// Adds the MCP server to the service collection with the provided options.
+ ///
+ ///
+ ///
+ ///
+ public static IMcpServerBuilder AddMcpServer(this IServiceCollection services, McpServerOptions serverOptions)
+ {
+ services.AddSingleton(serverOptions);
+ services.AddSingleton();
+ services.AddHostedService();
+ services.AddOptions();
+
+ services.AddSingleton(sp => sp.GetRequiredService().CreateServer());
+
+ return new DefaultMcpServerBuilder(services);
+ }
+
+ private static McpServerOptions CreateDefaultServerOptions()
+ {
+ var assemblyName = Assembly.GetEntryAssembly()?.GetName();
+
+ return new McpServerOptions()
+ {
+ ServerInfo = new Implementation() { Name = assemblyName?.Name ?? "McpServer", Version = assemblyName?.Version?.ToString() ?? "1.0.0" },
+ Capabilities = new ServerCapabilities()
+ {
+ Tools = new(),
+ Resources = new(),
+ Prompts = new(),
+ },
+ ProtocolVersion = "2024-11-05"
+ };
+ }
+}
diff --git a/src/mcpdotnet/Hosting/McpServerHostedService.cs b/src/mcpdotnet/Hosting/McpServerHostedService.cs
new file mode 100644
index 0000000..d9b0b0f
--- /dev/null
+++ b/src/mcpdotnet/Hosting/McpServerHostedService.cs
@@ -0,0 +1,28 @@
+using McpDotNet.Server;
+using Microsoft.Extensions.Hosting;
+
+namespace McpDotNet.Hosting;
+
+///
+/// Hosted service for the MCP server.
+///
+public class McpServerHostedService : BackgroundService
+{
+ private readonly IMcpServer _server;
+
+ ///
+ /// Creates a new instance of the McpServerHostedService.
+ ///
+ /// The MCP server instance
+ ///
+ public McpServerHostedService(IMcpServer server)
+ {
+ _server = server ?? throw new ArgumentNullException(nameof(server));
+ }
+
+ ///
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ await _server.StartAsync(stoppingToken);
+ }
+}
diff --git a/src/mcpdotnet/Protocol/Transport/StdioServerTransport.cs b/src/mcpdotnet/Protocol/Transport/StdioServerTransport.cs
index a9820dc..7a7feb7 100644
--- a/src/mcpdotnet/Protocol/Transport/StdioServerTransport.cs
+++ b/src/mcpdotnet/Protocol/Transport/StdioServerTransport.cs
@@ -1,8 +1,9 @@
using System.Text.Json;
-using Microsoft.Extensions.Logging;
-using McpDotNet.Protocol.Messages;
-using McpDotNet.Utils.Json;
using McpDotNet.Logging;
+using McpDotNet.Protocol.Messages;
+using McpDotNet.Server;
+using McpDotNet.Utils.Json;
+using Microsoft.Extensions.Logging;
namespace McpDotNet.Protocol.Transport;
@@ -19,6 +20,16 @@ public sealed class StdioServerTransport : TransportBase, IServerTransport
private string EndpointName => $"Server (stdio) ({_serverName})";
+ ///
+ /// Initializes a new instance of the StdioServerTransport class.
+ ///
+ /// The server options.
+ /// A logger factory for creating loggers.
+ public StdioServerTransport(McpServerOptions serverOptions, ILoggerFactory loggerFactory)
+ : this(serverOptions.ServerInfo.Name, loggerFactory)
+ {
+ }
+
///
/// Initializes a new instance of the StdioServerTransport class.
///
@@ -64,7 +75,7 @@ public sealed class StdioServerTransport : TransportBase, IServerTransport
{
var json = JsonSerializer.Serialize(message, _jsonOptions);
_logger.TransportSendingMessage(EndpointName, id, json);
-
+
using (Console.OpenStandardOutput())
{
await Console.Out.WriteLineAsync(json.AsMemory(), cancellationToken);
@@ -159,7 +170,7 @@ public sealed class StdioServerTransport : TransportBase, IServerTransport
private async Task CleanupAsync(CancellationToken cancellationToken)
{
_logger.TransportCleaningUp(EndpointName);
-
+
_shutdownCts?.Cancel();
_shutdownCts?.Dispose();
_shutdownCts = null;
diff --git a/src/mcpdotnet/Server/IMcpServerFactory.cs b/src/mcpdotnet/Server/IMcpServerFactory.cs
new file mode 100644
index 0000000..a8c1e70
--- /dev/null
+++ b/src/mcpdotnet/Server/IMcpServerFactory.cs
@@ -0,0 +1,13 @@
+namespace McpDotNet.Server;
+
+///
+/// Factory for creating instances.
+///
+public interface IMcpServerFactory
+{
+ ///
+ /// Creates a new server instance.
+ ///
+ ///
+ IMcpServer CreateServer();
+}
diff --git a/src/mcpdotnet/Server/McpServer.cs b/src/mcpdotnet/Server/McpServer.cs
index 4355c9b..c253255 100644
--- a/src/mcpdotnet/Server/McpServer.cs
+++ b/src/mcpdotnet/Server/McpServer.cs
@@ -1,11 +1,11 @@
using System.Text.Json.Nodes;
-using Microsoft.Extensions.Logging;
-using McpDotNet.Protocol.Types;
-using McpDotNet.Shared;
-using McpDotNet.Protocol.Transport;
using McpDotNet.Logging;
using McpDotNet.Protocol.Messages;
+using McpDotNet.Protocol.Transport;
+using McpDotNet.Protocol.Types;
+using McpDotNet.Shared;
+using Microsoft.Extensions.Logging;
namespace McpDotNet.Server;
@@ -22,22 +22,22 @@ internal class McpServer : McpJsonRpcEndpoint, IMcpServer
///
/// Transport to use for the server
/// Configuration options for this server, including capabilities.
- /// Make sure to accurately reflect exactly what capabilities the server supports and does not support.
- /// Optional server instructions to send to clients
+ /// Make sure to accurately reflect exactly what capabilities the server supports and does not support.
/// Logger factory to use for logging
///
- public McpServer(IServerTransport transport, McpServerOptions options, string? serverInstructions, ILoggerFactory loggerFactory)
+ public McpServer(IServerTransport transport, McpServerOptions options, ILoggerFactory loggerFactory)
: base(transport, loggerFactory)
{
_serverTransport = transport;
_options = options;
_logger = loggerFactory.CreateLogger();
- ServerInstructions = serverInstructions;
+ ServerInstructions = options.ServerInstructions;
if (options.Capabilities?.Tools != null)
{
SetRequestHandler("tools/list",
- async (request) => {
+ async (request) =>
+ {
if (ListToolsHandler == null)
{
// Setting the capability, but not a handler means we have nothing to return to the server
@@ -49,7 +49,8 @@ internal class McpServer : McpJsonRpcEndpoint, IMcpServer
});
SetRequestHandler("tools/call",
- async (request) => {
+ async (request) =>
+ {
if (CallToolHandler == null)
{
// Setting the capability, but not a handler means we have nothing to return to the server
@@ -265,7 +266,7 @@ internal class McpServer : McpJsonRpcEndpoint, IMcpServer
if (ClientCapabilities?.Roots == null)
{
throw new McpServerException("Client does not support roots");
- }
+ }
return await SendRequestAsync(
new JsonRpcRequest
diff --git a/src/mcpdotnet/Server/McpServerDelegates.cs b/src/mcpdotnet/Server/McpServerDelegates.cs
new file mode 100644
index 0000000..fc5b691
--- /dev/null
+++ b/src/mcpdotnet/Server/McpServerDelegates.cs
@@ -0,0 +1,88 @@
+using McpDotNet.Protocol.Types;
+
+namespace McpDotNet.Server;
+
+///
+/// Container for delegates that can be applied to an MCP server.
+///
+public class McpServerDelegates
+{
+ ///
+ /// Gets or sets the handler for list tools requests.
+ ///
+ public Func>? ListToolsHandler { get; set; }
+
+ ///
+ /// Gets or sets the handler for call tool requests.
+ ///
+ public Func>? CallToolHandler { get; set; }
+
+ ///
+ /// Gets or sets the handler for list prompts requests.
+ ///
+ public Func>? ListPromptsHandler { get; set; }
+
+ ///
+ /// Gets or sets the handler for get prompt requests.
+ ///
+ public Func>? GetPromptHandler { get; set; }
+
+ ///
+ /// Gets or sets the handler for list resources requests.
+ ///
+ public Func>? ListResourcesHandler { get; set; }
+
+ ///
+ /// Gets or sets the handler for read resources requests.
+ ///
+ public Func>? ReadResourceHandler { get; set; }
+
+ ///
+ /// Gets or sets the handler for get resources requests.
+ ///
+ public Func>? GetCompletionHandler { get; set; }
+
+ ///
+ /// Gets or sets the handler for subscribe to resources messages.
+ ///
+ public Func? SubscribeToResourcesHandler { get; set; }
+
+ ///
+ /// Gets or sets the handler for subscribe to resources messages.
+ ///
+ public Func? UnsubscribeFromResourcesHandler { get; set; }
+
+ ///
+ /// Applies the delegates to the server.
+ ///
+ ///
+ public void Apply(IMcpServer server)
+ {
+ if (ListToolsHandler != null)
+ server.ListToolsHandler = ListToolsHandler;
+
+ if (CallToolHandler != null)
+ server.CallToolHandler = CallToolHandler;
+
+ if (ListPromptsHandler != null)
+ server.ListPromptsHandler = ListPromptsHandler;
+
+ if (GetPromptHandler != null)
+ server.GetPromptHandler = GetPromptHandler;
+
+ if (ListResourcesHandler != null)
+ server.ListResourcesHandler = ListResourcesHandler;
+
+ if (ReadResourceHandler != null)
+ server.ReadResourceHandler = ReadResourceHandler;
+
+ if (GetCompletionHandler != null)
+ server.GetCompletionHandler = GetCompletionHandler;
+
+ if (SubscribeToResourcesHandler != null)
+ server.SubscribeToResourcesHandler = SubscribeToResourcesHandler;
+
+ if (UnsubscribeFromResourcesHandler != null)
+ server.UnsubscribeFromResourcesHandler = UnsubscribeFromResourcesHandler;
+ }
+}
diff --git a/src/mcpdotnet/Server/McpServerFactory.cs b/src/mcpdotnet/Server/McpServerFactory.cs
index 858fd4f..37cfd93 100644
--- a/src/mcpdotnet/Server/McpServerFactory.cs
+++ b/src/mcpdotnet/Server/McpServerFactory.cs
@@ -1,5 +1,6 @@
-using Microsoft.Extensions.Logging;
-using McpDotNet.Protocol.Transport;
+using McpDotNet.Protocol.Transport;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
namespace McpDotNet.Server;
@@ -13,12 +14,12 @@ namespace McpDotNet.Server;
///
/// You must register handlers for all supported capabilities on the server instance, before calling BeginListeningAsync.
///
-public class McpServerFactory
+public class McpServerFactory : IMcpServerFactory
{
private readonly IServerTransport _serverTransport;
private readonly McpServerOptions _options;
private readonly ILoggerFactory _loggerFactory;
- private readonly string? _serverInstructions;
+ private readonly McpServerDelegates? _serverDelegates;
///
/// Initializes a new instance of the class.
@@ -26,15 +27,14 @@ public class McpServerFactory
/// Transport to use for the server
/// Configuration options for this server, including capabilities.
/// Make sure to accurately reflect exactly what capabilities the server supports and does not support.
- /// Optional server instructions to send to clients
/// Logger factory to use for logging
- public McpServerFactory(IServerTransport serverTransport, McpServerOptions options, ILoggerFactory loggerFactory,
- string? serverInstructions = null)
+ ///
+ public McpServerFactory(IServerTransport serverTransport, McpServerOptions options, ILoggerFactory loggerFactory, IOptions? serverDelegates = null)
{
- _serverTransport = serverTransport;
- _options = options;
- _loggerFactory = loggerFactory;
- _serverInstructions = serverInstructions;
+ _serverTransport = serverTransport ?? throw new ArgumentNullException(nameof(serverTransport));
+ _options = options ?? throw new ArgumentNullException(nameof(options));
+ _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
+ _serverDelegates = serverDelegates?.Value;
}
///
@@ -44,6 +44,10 @@ public class McpServerFactory
///
public IMcpServer CreateServer()
{
- return new McpServer(_serverTransport, _options, _serverInstructions, _loggerFactory);
+ var server = new McpServer(_serverTransport, _options, _loggerFactory);
+
+ _serverDelegates?.Apply(server);
+
+ return server;
}
}
diff --git a/src/mcpdotnet/Server/McpServerOptions.cs b/src/mcpdotnet/Server/McpServerOptions.cs
index 37b577c..76f5e1d 100644
--- a/src/mcpdotnet/Server/McpServerOptions.cs
+++ b/src/mcpdotnet/Server/McpServerOptions.cs
@@ -29,4 +29,9 @@ public record McpServerOptions
/// Timeout for initialization sequence.
///
public TimeSpan InitializationTimeout { get; init; } = TimeSpan.FromSeconds(60);
+
+ ///
+ /// Optional server instructions to send to clients
+ ///
+ public string ServerInstructions { get; init; } = string.Empty;
}
diff --git a/src/mcpdotnet/mcpdotnet.csproj b/src/mcpdotnet/mcpdotnet.csproj
index f0cfde9..99af1e7 100644
--- a/src/mcpdotnet/mcpdotnet.csproj
+++ b/src/mcpdotnet/mcpdotnet.csproj
@@ -20,7 +20,7 @@
MIT
mcpdotnet
- mcpdotnet
+ McpDotNet
true
true
snupkg
@@ -35,6 +35,7 @@
+
diff --git a/tests/mcpdotnet.TestServer/Program.cs b/tests/mcpdotnet.TestServer/Program.cs
index 9bd5b4b..3c942ea 100644
--- a/tests/mcpdotnet.TestServer/Program.cs
+++ b/tests/mcpdotnet.TestServer/Program.cs
@@ -1,10 +1,9 @@
-using McpDotNet.Protocol.Transport;
+using System.Text;
+using McpDotNet.Protocol.Transport;
using McpDotNet.Protocol.Types;
using McpDotNet.Server;
using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Abstractions;
using Serilog;
-using System.Text;
internal class Program
{
@@ -38,11 +37,11 @@ internal class Program
Resources = new(),
Prompts = new(),
},
- ProtocolVersion = "2024-11-05"
+ ProtocolVersion = "2024-11-05",
+ ServerInstructions = "This is a test server with only stub functionality",
};
var loggerFactory = CreateLoggerFactory();
- McpServerFactory factory = new McpServerFactory(new StdioServerTransport("TestServer", loggerFactory), options, loggerFactory,
- "This is a test server with only stub functionality");
+ McpServerFactory factory = new McpServerFactory(new StdioServerTransport("TestServer", loggerFactory), options, loggerFactory);
IMcpServer server = factory.CreateServer();
Console.WriteLine("Server object created, registering handlers.");
@@ -51,7 +50,7 @@ internal class Program
static CreateMessageRequestParams CreateRequestSamplingParams(string context, string uri, int maxTokens = 100)
{
return new CreateMessageRequestParams()
- {
+ {
Messages = [new SamplingMessage()
{
Role = Role.User,
@@ -61,10 +60,10 @@ internal class Program
Text = $"Resource {uri} context: {context}"
}
}],
- SystemPrompt = "You are a helpful test server.",
- MaxTokens = maxTokens,
- Temperature = 0.7f,
- IncludeContext = ContextInclusion.ThisServer
+ SystemPrompt = "You are a helpful test server.",
+ MaxTokens = maxTokens,
+ Temperature = 0.7f,
+ IncludeContext = ContextInclusion.ThisServer
};
}
#endregion
@@ -74,9 +73,9 @@ internal class Program
{
return Task.FromResult(new ListToolsResult()
{
- Tools =
+ Tools =
[
- new Tool()
+ new Tool()
{
Name = "echo",
Description = "Echoes the input back to the client.",
@@ -122,8 +121,8 @@ internal class Program
}
else if (request.Name == "sampleLLM")
{
- if (request.Arguments is null ||
- !request.Arguments.TryGetValue("prompt", out var prompt) ||
+ if (request.Arguments is null ||
+ !request.Arguments.TryGetValue("prompt", out var prompt) ||
!request.Arguments.TryGetValue("maxTokens", out var maxTokens))
{
throw new McpServerException("Missing required arguments 'prompt' and 'maxTokens'");
@@ -200,10 +199,10 @@ internal class Program
throw new McpServerException("Invalid cursor");
}
}
-
+
int endIndex = Math.Min(startIndex + pageSize, resources.Count);
string? nextCursor = null;
-
+
if (endIndex < resources.Count)
{
nextCursor = Convert.ToBase64String(Encoding.UTF8.GetBytes(endIndex.ToString()));
diff --git a/tests/mcpdotnet.TestServer/mcpdotnet.TestServer.csproj b/tests/mcpdotnet.TestServer/mcpdotnet.TestServer.csproj
index 6451d08..ef2c91a 100644
--- a/tests/mcpdotnet.TestServer/mcpdotnet.TestServer.csproj
+++ b/tests/mcpdotnet.TestServer/mcpdotnet.TestServer.csproj
@@ -9,6 +9,7 @@
+
diff --git a/tests/mcpdotnet.Tests/Server/McpServerDelegatesTests.cs b/tests/mcpdotnet.Tests/Server/McpServerDelegatesTests.cs
new file mode 100644
index 0000000..3516eb5
--- /dev/null
+++ b/tests/mcpdotnet.Tests/Server/McpServerDelegatesTests.cs
@@ -0,0 +1,38 @@
+using McpDotNet.Protocol.Types;
+using McpDotNet.Server;
+using Moq;
+
+namespace mcpdotnet.Tests.Server;
+
+public class McpServerDelegatesTests
+{
+ [Fact]
+ public void Applies_All_Given_Delegates()
+ {
+ var container = new McpServerDelegates();
+ var server = new Mock();
+ server.SetupAllProperties();
+
+ container.ListToolsHandler = (p, c) => Task.FromResult(new ListToolsResult());
+ container.CallToolHandler = (p, c) => Task.FromResult(new CallToolResponse());
+ container.ListPromptsHandler = (p, c) => Task.FromResult(new ListPromptsResult());
+ container.GetPromptHandler = (p, c) => Task.FromResult(new GetPromptResult());
+ container.ListResourcesHandler = (p, c) => Task.FromResult(new ListResourcesResult());
+ container.ReadResourceHandler = (p, c) => Task.FromResult(new ReadResourceResult());
+ container.GetCompletionHandler = (p, c) => Task.FromResult(new CompleteResult());
+ container.SubscribeToResourcesHandler = (s, c) => Task.CompletedTask;
+ container.UnsubscribeFromResourcesHandler = (s, c) => Task.CompletedTask;
+
+ container.Apply(server.Object);
+
+ Assert.Equal(container.ListToolsHandler, server.Object.ListToolsHandler);
+ Assert.Equal(container.CallToolHandler, server.Object.CallToolHandler);
+ Assert.Equal(container.ListPromptsHandler, server.Object.ListPromptsHandler);
+ Assert.Equal(container.GetPromptHandler, server.Object.GetPromptHandler);
+ Assert.Equal(container.ListResourcesHandler, server.Object.ListResourcesHandler);
+ Assert.Equal(container.ReadResourceHandler, server.Object.ReadResourceHandler);
+ Assert.Equal(container.GetCompletionHandler, server.Object.GetCompletionHandler);
+ Assert.Equal(container.SubscribeToResourcesHandler, server.Object.SubscribeToResourcesHandler);
+ Assert.Equal(container.UnsubscribeFromResourcesHandler, server.Object.UnsubscribeFromResourcesHandler);
+ }
+}