diff --git a/src/ModelContextProtocol.Core/Client/McpClientImpl.cs b/src/ModelContextProtocol.Core/Client/McpClientImpl.cs
index 6397188..729185a 100644
--- a/src/ModelContextProtocol.Core/Client/McpClientImpl.cs
+++ b/src/ModelContextProtocol.Core/Client/McpClientImpl.cs
@@ -26,6 +26,7 @@ internal sealed partial class McpClientImpl : McpClient
private ServerCapabilities? _serverCapabilities;
private Implementation? _serverInfo;
private string? _serverInstructions;
+ private string? _negotiatedProtocolVersion;
private bool _disposed;
@@ -112,6 +113,9 @@ internal sealed partial class McpClientImpl : McpClient
///
public override string? SessionId => _transport.SessionId;
+ ///
+ public override string? NegotiatedProtocolVersion => _negotiatedProtocolVersion;
+
///
public override ServerCapabilities ServerCapabilities => _serverCapabilities ?? throw new InvalidOperationException("The client is not connected.");
@@ -177,6 +181,8 @@ internal sealed partial class McpClientImpl : McpClient
throw new McpException($"Server protocol version mismatch. Expected {requestProtocol}, got {initializeResponse.ProtocolVersion}");
}
+ _negotiatedProtocolVersion = initializeResponse.ProtocolVersion;
+
// Send initialized notification
await this.SendNotificationAsync(
NotificationMethods.InitializedNotification,
diff --git a/src/ModelContextProtocol.Core/McpSession.cs b/src/ModelContextProtocol.Core/McpSession.cs
index 241c36d..429fdbf 100644
--- a/src/ModelContextProtocol.Core/McpSession.cs
+++ b/src/ModelContextProtocol.Core/McpSession.cs
@@ -38,6 +38,15 @@ public abstract partial class McpSession : IMcpEndpoint, IAsyncDisposable
///
public abstract string? SessionId { get; }
+ ///
+ /// Gets the negotiated protocol version for the current MCP session.
+ ///
+ ///
+ /// Returns the protocol version negotiated during session initialization,
+ /// or if initialization hasn't yet occurred.
+ ///
+ public abstract string? NegotiatedProtocolVersion { get; }
+
///
/// Sends a JSON-RPC request to the connected session and waits for a response.
///
@@ -45,7 +54,7 @@ public abstract partial class McpSession : IMcpEndpoint, IAsyncDisposable
/// The to monitor for cancellation requests. The default is .
/// A task containing the session's response.
/// The transport is not connected, or another error occurs during request processing.
- /// An error occured during request processing.
+ /// An error occurred during request processing.
///
/// This method provides low-level access to send raw JSON-RPC requests. For most use cases,
/// consider using the strongly-typed methods that provide a more convenient API.
diff --git a/src/ModelContextProtocol.Core/Server/DestinationBoundMcpServer.cs b/src/ModelContextProtocol.Core/Server/DestinationBoundMcpServer.cs
index f74dc29..bbbc45d 100644
--- a/src/ModelContextProtocol.Core/Server/DestinationBoundMcpServer.cs
+++ b/src/ModelContextProtocol.Core/Server/DestinationBoundMcpServer.cs
@@ -6,6 +6,7 @@ namespace ModelContextProtocol.Server;
internal sealed class DestinationBoundMcpServer(McpServerImpl server, ITransport? transport) : McpServer
{
public override string? SessionId => transport?.SessionId ?? server.SessionId;
+ public override string? NegotiatedProtocolVersion => server.NegotiatedProtocolVersion;
public override ClientCapabilities? ClientCapabilities => server.ClientCapabilities;
public override Implementation? ClientInfo => server.ClientInfo;
public override McpServerOptions ServerOptions => server.ServerOptions;
diff --git a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
index 1ece8af..f4f9e8b 100644
--- a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
@@ -7,8 +7,6 @@ using System.Text.Json.Serialization.Metadata;
namespace ModelContextProtocol.Server;
-// TODO: Fix merge conflicts in this file.
-
///
internal sealed partial class McpServerImpl : McpServer
{
@@ -31,6 +29,7 @@ internal sealed partial class McpServerImpl : McpServer
private Implementation? _clientInfo;
private readonly string _serverOnlyEndpointName;
+ private string? _negotiatedProtocolVersion;
private string _endpointName;
private int _started;
@@ -116,6 +115,9 @@ internal sealed partial class McpServerImpl : McpServer
///
public override string? SessionId => _sessionTransport.SessionId;
+ ///
+ public override string? NegotiatedProtocolVersion => _negotiatedProtocolVersion;
+
///
public ServerCapabilities ServerCapabilities { get; } = new();
@@ -212,6 +214,8 @@ internal sealed partial class McpServerImpl : McpServer
McpSessionHandler.LatestProtocolVersion;
}
+ _negotiatedProtocolVersion = protocolVersion;
+
return new InitializeResult
{
ProtocolVersion = protocolVersion,
diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs
index f9aa5a5..5da3714 100644
--- a/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs
+++ b/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs
@@ -52,6 +52,7 @@ public abstract class HttpServerIntegrationTests : LoggedTest, IClassFixture s));
}
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("2025-03-26")]
+ public async Task ReturnsNegotiatedProtocolVersion(string? protocolVersion)
+ {
+ await using McpClient client = await CreateMcpClientForServer(new() { ProtocolVersion = protocolVersion });
+ Assert.Equal(protocolVersion ?? "2025-06-18", client.NegotiatedProtocolVersion);
+ }
}
\ No newline at end of file
diff --git a/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs b/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs
index 2116884..e2f0380 100644
--- a/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs
+++ b/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs
@@ -52,8 +52,12 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture
+ assertResult: (_, response) =>
{
JsonObject jObj = Assert.IsType(response);
Assert.Empty(jObj);
@@ -247,13 +248,14 @@ public class McpServerTests : LoggedTest
serverCapabilities: null,
method: RequestMethods.Initialize,
configureOptions: null,
- assertResult: response =>
+ assertResult: (server, response) =>
{
var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Equal(expectedAssemblyName.Name, result.ServerInfo.Name);
Assert.Equal(expectedAssemblyName.Version?.ToString() ?? "1.0.0", result.ServerInfo.Version);
Assert.Equal("2024", result.ProtocolVersion);
+ Assert.Equal("2024", server.NegotiatedProtocolVersion);
});
}
@@ -279,7 +281,7 @@ public class McpServerTests : LoggedTest
},
method: RequestMethods.CompletionComplete,
configureOptions: null,
- assertResult: response =>
+ assertResult: (_, response) =>
{
var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Completion);
@@ -316,7 +318,7 @@ public class McpServerTests : LoggedTest
},
RequestMethods.ResourcesTemplatesList,
configureOptions: null,
- assertResult: response =>
+ assertResult: (_, response) =>
{
var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.ResourceTemplates);
@@ -345,7 +347,7 @@ public class McpServerTests : LoggedTest
},
RequestMethods.ResourcesList,
configureOptions: null,
- assertResult: response =>
+ assertResult: (_, response) =>
{
var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Resources);
@@ -380,7 +382,7 @@ public class McpServerTests : LoggedTest
},
method: RequestMethods.ResourcesRead,
configureOptions: null,
- assertResult: response =>
+ assertResult: (_, response) =>
{
var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Contents);
@@ -417,7 +419,7 @@ public class McpServerTests : LoggedTest
},
method: RequestMethods.PromptsList,
configureOptions: null,
- assertResult: response =>
+ assertResult: (_, response) =>
{
var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Prompts);
@@ -446,7 +448,7 @@ public class McpServerTests : LoggedTest
},
method: RequestMethods.PromptsGet,
configureOptions: null,
- assertResult: response =>
+ assertResult: (_, response) =>
{
var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
@@ -480,7 +482,7 @@ public class McpServerTests : LoggedTest
},
method: RequestMethods.ToolsList,
configureOptions: null,
- assertResult: response =>
+ assertResult: (_, response) =>
{
var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
@@ -515,7 +517,7 @@ public class McpServerTests : LoggedTest
},
method: RequestMethods.ToolsCall,
configureOptions: null,
- assertResult: response =>
+ assertResult: (_, response) =>
{
var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
@@ -530,7 +532,7 @@ public class McpServerTests : LoggedTest
await Succeeds_Even_If_No_Handler_Assigned(new ServerCapabilities { Tools = new() }, RequestMethods.ToolsCall, "CallTool handler not configured");
}
- private async Task Can_Handle_Requests(ServerCapabilities? serverCapabilities, string method, Action? configureOptions, Action assertResult)
+ private async Task Can_Handle_Requests(ServerCapabilities? serverCapabilities, string method, Action? configureOptions, Action assertResult)
{
await using var transport = new TestServerTransport();
var options = CreateOptions(serverCapabilities);
@@ -559,7 +561,7 @@ public class McpServerTests : LoggedTest
var response = await receivedMessage.Task.WaitAsync(TimeSpan.FromSeconds(5));
Assert.NotNull(response);
- assertResult(response.Result);
+ assertResult(server, response.Result);
await transport.DisposeAsync();
await runTask;
@@ -682,6 +684,7 @@ public class McpServerTests : LoggedTest
public override ValueTask DisposeAsync() => default;
public override string? SessionId => throw new NotImplementedException();
+ public override string? NegotiatedProtocolVersion => throw new NotImplementedException();
public override Implementation? ClientInfo => throw new NotImplementedException();
public override IServiceProvider? Services => throw new NotImplementedException();
public override LoggingLevel? LoggingLevel => throw new NotImplementedException();