Send MCP-Protocol-Version header in Streamable HTTP client transport (#500)

This commit is contained in:
Stephen Halter 2025-06-17 14:27:26 -07:00 • committed by GitHub
commit befa31d5c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -27,6 +27,7 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
private readonly CancellationTokenSource _connectionCts;
private readonly ILogger _logger;
private string? _negotiatedProtocolVersion;
private Task? _getReceiveTask;
public StreamableHttpClientSessionTransport(
@ -84,7 +85,7 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
},
};
CopyAdditionalHeaders(httpRequestMessage.Headers, _options.AdditionalHeaders, SessionId);
CopyAdditionalHeaders(httpRequestMessage.Headers, _options.AdditionalHeaders, SessionId, _negotiatedProtocolVersion);
var response = await _httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
@ -118,14 +119,17 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
throw new McpException($"Streamable HTTP POST response completed without a reply to request with ID: {rpcRequest.Id}");
}
if (rpcRequest.Method == RequestMethods.Initialize && rpcResponseOrError is JsonRpcResponse)
if (rpcRequest.Method == RequestMethods.Initialize && rpcResponseOrError is JsonRpcResponse initResponse)
{
// We've successfully initialized! Copy session-id and start GET request if any.
if (response.Headers.TryGetValues("mcp-session-id", out var sessionIdValues))
// We've successfully initialized! Copy session-id and protocol version, then start GET request if any.
if (response.Headers.TryGetValues("Mcp-Session-Id", out var sessionIdValues))
{
SessionId = sessionIdValues.FirstOrDefault();
}
var initializeResult = JsonSerializer.Deserialize(initResponse.Result, McpJsonUtilities.JsonContext.Default.InitializeResult);
_negotiatedProtocolVersion = initializeResult?.ProtocolVersion;
_getReceiveTask = ReceiveUnsolicitedMessagesAsync();
}
@ -169,7 +173,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, SessionId);
CopyAdditionalHeaders(request.Headers, _options.AdditionalHeaders, SessionId, _negotiatedProtocolVersion);
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, _connectionCts.Token).ConfigureAwait(false);
@ -244,11 +248,20 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
}
}
internal static void CopyAdditionalHeaders(HttpRequestHeaders headers, IDictionary<string, string>? additionalHeaders, string? sessionId = null)
internal static void CopyAdditionalHeaders(
HttpRequestHeaders headers,
IDictionary<string, string>? additionalHeaders,
string? sessionId = null,
string? protocolVersion = null)
{
if (sessionId is not null)
{
headers.Add("mcp-session-id", sessionId);
headers.Add("Mcp-Session-Id", sessionId);
}
if (protocolVersion is not null)
{
headers.Add("MCP-Protocol-Version", protocolVersion);
}
if (additionalHeaders is null)