Skip to content

Commit d54a239

Browse files
Expose a IMcpEndpoint.NegotiatedProtocolVersion property.
1 parent 15d1271 commit d54a239

14 files changed

Lines changed: 43 additions & 6 deletions

src/ModelContextProtocol.Core/Client/AutoDetectingClientSessionTransport.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public AutoDetectingClientSessionTransport(string endpointName, SseClientTranspo
4646
public ChannelReader<JsonRpcMessage> MessageReader => _messageChannel.Reader;
4747

4848
string? ITransport.SessionId => ActiveTransport?.SessionId;
49+
string? ITransport.NegotiatedProtocolVersion => ActiveTransport?.NegotiatedProtocolVersion;
4950

5051
/// <inheritdoc/>
5152
public Task SendMessageAsync(JsonRpcMessage message, CancellationToken cancellationToken = default)

src/ModelContextProtocol.Core/Client/McpClient.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,20 @@ public string? SessionId
107107
}
108108
}
109109

110+
/// <inheritdoc/>
111+
public string? NegotiatedProtocolVersion
112+
{
113+
get
114+
{
115+
if (_sessionTransport is null)
116+
{
117+
throw new InvalidOperationException("Must have already initialized a session when invoking this property.");
118+
}
119+
120+
return _sessionTransport.NegotiatedProtocolVersion;
121+
}
122+
}
123+
110124
/// <inheritdoc/>
111125
public ServerCapabilities ServerCapabilities => _serverCapabilities ?? throw new InvalidOperationException("The client is not connected.");
112126

src/ModelContextProtocol.Core/Client/StreamableHttpClientSessionTransport.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
using Microsoft.Extensions.Logging;
22
using Microsoft.Extensions.Logging.Abstractions;
3+
using ModelContextProtocol.Protocol;
34
using System.Net.Http.Headers;
45
using System.Net.ServerSentEvents;
56
using System.Text.Json;
6-
using ModelContextProtocol.Protocol;
77
using System.Threading.Channels;
88

99
namespace ModelContextProtocol.Client;
@@ -21,7 +21,6 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
2121
private readonly CancellationTokenSource _connectionCts;
2222
private readonly ILogger _logger;
2323

24-
private string? _negotiatedProtocolVersion;
2524
private Task? _getReceiveTask;
2625

2726
private readonly SemaphoreSlim _disposeLock = new(1, 1);
@@ -71,7 +70,7 @@ internal async Task<HttpResponseMessage> SendHttpRequestAsync(JsonRpcMessage mes
7170
},
7271
};
7372

74-
CopyAdditionalHeaders(httpRequestMessage.Headers, _options.AdditionalHeaders, SessionId, _negotiatedProtocolVersion);
73+
CopyAdditionalHeaders(httpRequestMessage.Headers, _options.AdditionalHeaders, SessionId, NegotiatedProtocolVersion);
7574

7675
var response = await _httpClient.SendAsync(httpRequestMessage, message, cancellationToken).ConfigureAwait(false);
7776

@@ -114,7 +113,7 @@ internal async Task<HttpResponseMessage> SendHttpRequestAsync(JsonRpcMessage mes
114113
}
115114

116115
var initializeResult = JsonSerializer.Deserialize(initResponse.Result, McpJsonUtilities.JsonContext.Default.InitializeResult);
117-
_negotiatedProtocolVersion = initializeResult?.ProtocolVersion;
116+
NegotiatedProtocolVersion = initializeResult?.ProtocolVersion;
118117

119118
_getReceiveTask = ReceiveUnsolicitedMessagesAsync();
120119
}
@@ -173,7 +172,7 @@ private async Task ReceiveUnsolicitedMessagesAsync()
173172
// Send a GET request to handle any unsolicited messages not sent over a POST response.
174173
using var request = new HttpRequestMessage(HttpMethod.Get, _options.Endpoint);
175174
request.Headers.Accept.Add(s_textEventStreamMediaType);
176-
CopyAdditionalHeaders(request.Headers, _options.AdditionalHeaders, SessionId, _negotiatedProtocolVersion);
175+
CopyAdditionalHeaders(request.Headers, _options.AdditionalHeaders, SessionId, NegotiatedProtocolVersion);
177176

178177
using var response = await _httpClient.SendAsync(request, message: null, _connectionCts.Token).ConfigureAwait(false);
179178

@@ -241,7 +240,7 @@ message is JsonRpcMessageWithId rpcResponseOrError &&
241240
private async Task SendDeleteRequest()
242241
{
243242
using var deleteRequest = new HttpRequestMessage(HttpMethod.Delete, _options.Endpoint);
244-
CopyAdditionalHeaders(deleteRequest.Headers, _options.AdditionalHeaders, SessionId, _negotiatedProtocolVersion);
243+
CopyAdditionalHeaders(deleteRequest.Headers, _options.AdditionalHeaders, SessionId, NegotiatedProtocolVersion);
245244

246245
try
247246
{

src/ModelContextProtocol.Core/IMcpEndpoint.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public interface IMcpEndpoint : IAsyncDisposable
3636
/// </remarks>
3737
string? SessionId { get; }
3838

39+
/// <summary>Gets the negotiated protocol version for the current MCP session, if available.</summary>
40+
string? NegotiatedProtocolVersion { get; }
41+
3942
/// <summary>
4043
/// Sends a JSON-RPC request to the connected endpoint and waits for a response.
4144
/// </summary>

src/ModelContextProtocol.Core/Protocol/ITransport.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ public interface ITransport : IAsyncDisposable
3333
/// </remarks>
3434
string? SessionId { get; }
3535

36+
/// <summary>Gets the negotiated protocol version for the current MCP session, if available.</summary>
37+
string? NegotiatedProtocolVersion { get; }
38+
3639
/// <summary>
3740
/// Gets a channel reader for receiving messages from the transport.
3841
/// </summary>

src/ModelContextProtocol.Core/Protocol/TransportBase.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ internal TransportBase(string name, Channel<JsonRpcMessage>? messageChannel, ILo
6262
/// <inheritdoc/>
6363
public virtual string? SessionId { get; protected set; }
6464

65+
/// <inheritdoc/>
66+
public virtual string? NegotiatedProtocolVersion { get; protected set; }
67+
6568
/// <summary>
6669
/// Gets the name that identifies this transport endpoint in logs.
6770
/// </summary>

src/ModelContextProtocol.Core/Server/DestinationBoundMcpServer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ internal sealed class DestinationBoundMcpServer(McpServer server, ITransport? tr
77
{
88
public string EndpointName => server.EndpointName;
99
public string? SessionId => transport?.SessionId ?? server.SessionId;
10+
public string? NegotiatedProtocolVersion => transport?.NegotiatedProtocolVersion ?? server.NegotiatedProtocolVersion;
1011
public ClientCapabilities? ClientCapabilities => server.ClientCapabilities;
1112
public Implementation? ClientInfo => server.ClientInfo;
1213
public McpServerOptions ServerOptions => server.ServerOptions;

src/ModelContextProtocol.Core/Server/McpServer.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ void Register<TPrimitive>(McpServerPrimitiveCollection<TPrimitive>? collection,
9999
/// <inheritdoc/>
100100
public string? SessionId => _sessionTransport.SessionId;
101101

102+
/// <inheritdoc/>
103+
public string? NegotiatedProtocolVersion => _sessionTransport.NegotiatedProtocolVersion;
104+
102105
/// <inheritdoc/>
103106
public ServerCapabilities ServerCapabilities { get; } = new();
104107

src/ModelContextProtocol.Core/Server/SseResponseStreamTransport.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ public async Task RunAsync(CancellationToken cancellationToken = default)
5454
/// <inheritdoc/>
5555
public string? SessionId { get; } = sessionId;
5656

57+
/// <inheritdoc/>
58+
public string? NegotiatedProtocolVersion => null;
59+
5760
/// <inheritdoc/>
5861
public async ValueTask DisposeAsync()
5962
{

src/ModelContextProtocol.Core/Server/StreamableHttpPostTransport.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ internal sealed class StreamableHttpPostTransport(StreamableHttpServerTransport
2121
public ChannelReader<JsonRpcMessage> MessageReader => throw new NotSupportedException("JsonRpcMessage.Context.RelatedTransport should only be used for sending messages.");
2222

2323
string? ITransport.SessionId => parentTransport.SessionId;
24+
string? ITransport.NegotiatedProtocolVersion => parentTransport.NegotiatedProtocolVersion;
2425

2526
/// <returns>
2627
/// True, if data was written to the respond body.

0 commit comments

Comments
 (0)