Pass session id's to MCP endpoints. (#466)

* Pass session id's to MCP endpoints.

* Update src/ModelContextProtocol/Client/McpClient.cs

Co-authored-by: Stephen Halter <halter73@gmail.com>

* Update src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs

Co-authored-by: Stephen Halter <halter73@gmail.com>

* Make init callback asynchronous.

* Address feedback.

---------

Co-authored-by: Stephen Halter <halter73@gmail.com>
This commit is contained in:
Eirik Tsarpalis 2025-06-12 16:56:57 +02:00 • committed by GitHub
commit d7d9eab3f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using ModelContextProtocol.AspNetCore.Stateless;
using ModelContextProtocol.Protocol;
@ -136,6 +135,7 @@ internal sealed class StreamableHttpHandler(
var transport = new StreamableHttpServerTransport
{
Stateless = true,
SessionId = sessionId,
};
session = await CreateSessionAsync(context, transport, sessionId, statelessSessionId);
}
@ -184,7 +184,10 @@ internal sealed class StreamableHttpHandler(
if (!HttpServerTransportOptions.Stateless)
{
sessionId = MakeNewSessionId();
transport = new();
transport = new()
{
SessionId = sessionId,
};
context.Response.Headers["mcp-session-id"] = sessionId;
}
else
@ -286,21 +289,19 @@ internal sealed class StreamableHttpHandler(
private void ScheduleStatelessSessionIdWrite(HttpContext context, StreamableHttpServerTransport transport)
{
context.Response.OnStarting(() =>
transport.OnInitRequestReceived = initRequestParams =>
{
var statelessId = new StatelessSessionId
{
ClientInfo = transport?.InitializeRequest?.ClientInfo,
ClientInfo = initRequestParams?.ClientInfo,
UserIdClaim = GetUserIdClaim(context.User),
};
var sessionJson = JsonSerializer.Serialize(statelessId, StatelessSessionIdJsonContext.Default.StatelessSessionId);
var sessionId = Protector.Protect(sessionJson);
context.Response.Headers["mcp-session-id"] = sessionId;
return Task.CompletedTask;
});
transport.SessionId = Protector.Protect(sessionJson);
context.Response.Headers["mcp-session-id"] = transport.SessionId;
return ValueTask.CompletedTask;
};
}
internal static Task RunSessionAsync(HttpContext httpContext, IMcpServer session, CancellationToken requestAborted)