Add stateless Streamable HTTP support (#392)
- It allows a single MCP session spanning multiple requests to be handled by different servers without sharing state - It does require the servers share data protection keys, but this is standard for ASP.NET Core cookies and antiforgery as well - It immediately throws for unsupported operations like sampling
This commit is contained in:
parent
ca3a8ab921
commit
ce9f73f43f
1 changed files with 785 additions and 230 deletions
|
|
@ -3,6 +3,7 @@ using ModelContextProtocol.Protocol.Types;
|
|||
using ModelContextProtocol.Server;
|
||||
using ModelContextProtocol.Utils.Json;
|
||||
using Serilog;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
|
|
@ -16,12 +17,11 @@ public class Program
|
|||
{
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Verbose() // Capture all log levels
|
||||
.WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "TestServer_.log"),
|
||||
.WriteTo.File(Path.Combine(AppContext.BaseDirectory, "logs", "TestServer_.log"),
|
||||
rollingInterval: RollingInterval.Day,
|
||||
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
|
||||
.CreateLogger();
|
||||
|
||||
var logsPath = Path.Combine(AppContext.BaseDirectory, "testserver.log");
|
||||
loggingBuilder.AddSerilog();
|
||||
}
|
||||
|
||||
|
|
@ -378,6 +378,26 @@ public class Program
|
|||
};
|
||||
}
|
||||
|
||||
private static void HandleStatelessMcp(IApplicationBuilder app)
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddLogging();
|
||||
serviceCollection.AddSingleton(app.ApplicationServices.GetRequiredService<ILoggerFactory>());
|
||||
serviceCollection.AddSingleton(app.ApplicationServices.GetRequiredService<DiagnosticListener>());
|
||||
serviceCollection.AddRoutingCore();
|
||||
|
||||
serviceCollection.AddMcpServer(ConfigureOptions).WithHttpTransport(options => options.Stateless = true);
|
||||
|
||||
var appBuilder = new ApplicationBuilder(serviceCollection.BuildServiceProvider());
|
||||
appBuilder.UseRouting();
|
||||
appBuilder.UseEndpoints(innerEndpoints =>
|
||||
{
|
||||
innerEndpoints.MapMcp("/stateless");
|
||||
});
|
||||
|
||||
app.Run(appBuilder.Build());
|
||||
}
|
||||
|
||||
public static async Task MainAsync(string[] args, ILoggerProvider? loggerProvider = null, IConnectionListenerFactory? kestrelTransport = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Console.WriteLine("Starting server...");
|
||||
|
|
@ -419,6 +439,9 @@ public class Program
|
|||
app.UseRouting();
|
||||
app.UseEndpoints(_ => { });
|
||||
|
||||
// Handle the /stateless endpoint if no other endpoints have been matched by the call to UseRouting above.
|
||||
HandleStatelessMcp(app);
|
||||
|
||||
app.MapMcp();
|
||||
|
||||
await app.RunAsync(cancellationToken);
|
||||
|
|
|
|||
Loading…
Reference in a new issue