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
3 changed files with 166 additions and 21 deletions

View file

@ -45,6 +45,8 @@ internal sealed partial class AutoDetectingClientSessionTransport : ITransport
public ChannelReader<JsonRpcMessage> MessageReader => _messageChannel.Reader;
string? ITransport.SessionId => ActiveTransport?.SessionId;
/// <inheritdoc/>
public Task SendMessageAsync(JsonRpcMessage message, CancellationToken cancellationToken = default)
{

View file

@ -1,5 +1,6 @@
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Protocol;
using System.Diagnostics;
using System.Text.Json;
namespace ModelContextProtocol.Client;
@ -93,6 +94,20 @@ internal sealed partial class McpClient : McpEndpoint, IMcpClient
}
}
/// <inheritdoc/>
public string? SessionId
{
get
{
if (_sessionTransport is null)
{
throw new InvalidOperationException("Must have already initialized a session when invoking this property.");
}
return _sessionTransport.SessionId;
}
}
/// <inheritdoc/>
public ServerCapabilities ServerCapabilities => _serverCapabilities ?? throw new InvalidOperationException("The client is not connected.");

View file

@ -27,7 +27,6 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
private readonly CancellationTokenSource _connectionCts;
private readonly ILogger _logger;
private string? _mcpSessionId;
private Task? _getReceiveTask;
public StreamableHttpClientSessionTransport(
@ -85,7 +84,7 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
},
};
CopyAdditionalHeaders(httpRequestMessage.Headers, _options.AdditionalHeaders, _mcpSessionId);
CopyAdditionalHeaders(httpRequestMessage.Headers, _options.AdditionalHeaders, SessionId);
var response = await _httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
@ -124,7 +123,7 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
// We've successfully initialized! Copy session-id and start GET request if any.
if (response.Headers.TryGetValues("mcp-session-id", out var sessionIdValues))
{
_mcpSessionId = sessionIdValues.FirstOrDefault();
SessionId = sessionIdValues.FirstOrDefault();
}
_getReceiveTask = ReceiveUnsolicitedMessagesAsync();
@ -170,7 +169,7 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
// Send a GET request to handle any unsolicited messages not sent over a POST response.
using var request = new HttpRequestMessage(HttpMethod.Get, _options.Endpoint);
request.Headers.Accept.Add(s_textEventStreamMediaType);
CopyAdditionalHeaders(request.Headers, _options.AdditionalHeaders, _mcpSessionId);
CopyAdditionalHeaders(request.Headers, _options.AdditionalHeaders, SessionId);
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, _connectionCts.Token).ConfigureAwait(false);