diff --git a/src/ModelContextProtocol.Core/Client/McpClient.cs b/src/ModelContextProtocol.Core/Client/McpClient.cs index 8dad491e..5c5ad2fe 100644 --- a/src/ModelContextProtocol.Core/Client/McpClient.cs +++ b/src/ModelContextProtocol.Core/Client/McpClient.cs @@ -129,11 +129,12 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default) try { // Send initialize request + string requestProtocol = _options.ProtocolVersion ?? McpSession.LatestProtocolVersion; var initializeResponse = await this.SendRequestAsync( RequestMethods.Initialize, new InitializeRequestParams { - ProtocolVersion = _options.ProtocolVersion, + ProtocolVersion = requestProtocol, Capabilities = _options.Capabilities ?? new ClientCapabilities(), ClientInfo = _options.ClientInfo ?? DefaultImplementation, }, @@ -154,10 +155,13 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default) _serverInstructions = initializeResponse.Instructions; // Validate protocol version - if (initializeResponse.ProtocolVersion != _options.ProtocolVersion) + bool isResponseProtocolValid = + _options.ProtocolVersion is { } optionsProtocol ? optionsProtocol == initializeResponse.ProtocolVersion : + McpSession.SupportedProtocolVersions.Contains(initializeResponse.ProtocolVersion); + if (!isResponseProtocolValid) { - LogServerProtocolVersionMismatch(EndpointName, _options.ProtocolVersion, initializeResponse.ProtocolVersion); - throw new McpException($"Server protocol version mismatch. Expected {_options.ProtocolVersion}, got {initializeResponse.ProtocolVersion}"); + LogServerProtocolVersionMismatch(EndpointName, requestProtocol, initializeResponse.ProtocolVersion); + throw new McpException($"Server protocol version mismatch. Expected {requestProtocol}, got {initializeResponse.ProtocolVersion}"); } // Send initialized notification diff --git a/src/ModelContextProtocol.Core/Client/McpClientOptions.cs b/src/ModelContextProtocol.Core/Client/McpClientOptions.cs index 2a5d2a84..96f36bbc 100644 --- a/src/ModelContextProtocol.Core/Client/McpClientOptions.cs +++ b/src/ModelContextProtocol.Core/Client/McpClientOptions.cs @@ -34,11 +34,18 @@ public class McpClientOptions /// Gets or sets the protocol version to request from the server, using a date-based versioning scheme. /// /// + /// /// The protocol version is a key part of the initialization handshake. The client and server must - /// agree on a compatible protocol version to communicate successfully. If the server doesn't support - /// the requested version, it will respond with a version mismatch error. + /// agree on a compatible protocol version to communicate successfully. + /// + /// + /// If non-, this version will be sent to the server, and the handshake + /// will fail if the version in the server's response does not match this version. + /// If , the client will request the latest version supported by the server + /// but will allow any supported version that the server advertizes in its response. + /// /// - public string ProtocolVersion { get; set; } = "2024-11-05"; + public string? ProtocolVersion { get; set; } /// /// Gets or sets a timeout for the client-server initialization handshake sequence. diff --git a/src/ModelContextProtocol.Core/McpSession.cs b/src/ModelContextProtocol.Core/McpSession.cs index 248538c3..8a0ae6b1 100644 --- a/src/ModelContextProtocol.Core/McpSession.cs +++ b/src/ModelContextProtocol.Core/McpSession.cs @@ -28,6 +28,16 @@ internal sealed partial class McpSession : IDisposable private static readonly Histogram s_serverOperationDuration = Diagnostics.CreateDurationHistogram( "mcp.server.operation.duration", "Measures the duration of inbound message processing.", longBuckets: false); + /// The latest version of the protocol supported by this implementation. + internal const string LatestProtocolVersion = "2025-03-26"; + + /// All protocol versions supported by this implementation. + internal static readonly string[] SupportedProtocolVersions = + [ + "2024-11-05", + LatestProtocolVersion, + ]; + private readonly bool _isServer; private readonly string _transportKind; private readonly ITransport _transport; diff --git a/src/ModelContextProtocol.Core/Protocol/PaginatedRequest.cs b/src/ModelContextProtocol.Core/Protocol/PaginatedRequest.cs index 89b17398..3e25b3b6 100644 --- a/src/ModelContextProtocol.Core/Protocol/PaginatedRequest.cs +++ b/src/ModelContextProtocol.Core/Protocol/PaginatedRequest.cs @@ -6,7 +6,7 @@ namespace ModelContextProtocol.Protocol; /// Provides a base class for paginated requests. /// /// -/// See the schema for details +/// See the schema for details /// public class PaginatedRequestParams : RequestParams { diff --git a/src/ModelContextProtocol.Core/Server/McpServer.cs b/src/ModelContextProtocol.Core/Server/McpServer.cs index 338c631f..808300f0 100644 --- a/src/ModelContextProtocol.Core/Server/McpServer.cs +++ b/src/ModelContextProtocol.Core/Server/McpServer.cs @@ -161,9 +161,20 @@ private void ConfigureInitialize(McpServerOptions options) UpdateEndpointNameWithClientInfo(); GetSessionOrThrow().EndpointName = EndpointName; + // Negotiate a protocol version. If the server options provide one, use that. + // Otherwise, try to use whatever the client requested as long as it's supported. + // If it's not supported, fall back to the latest supported version. + string? protocolVersion = options.ProtocolVersion; + if (protocolVersion is null) + { + protocolVersion = request?.ProtocolVersion is string clientProtocolVersion && McpSession.SupportedProtocolVersions.Contains(clientProtocolVersion) ? + clientProtocolVersion : + McpSession.LatestProtocolVersion; + } + return new InitializeResult { - ProtocolVersion = options.ProtocolVersion, + ProtocolVersion = protocolVersion, Instructions = options.ServerInstructions, ServerInfo = options.ServerInfo ?? DefaultImplementation, Capabilities = ServerCapabilities ?? new(), diff --git a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs index 3d6de61b..e84a012c 100644 --- a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs +++ b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs @@ -32,8 +32,11 @@ public class McpServerOptions /// /// The protocol version defines which features and message formats this server supports. /// This uses a date-based versioning scheme in the format "YYYY-MM-DD". + /// If , the server will advertize to the client the version requested + /// by the client if that version is known to be supported, and otherwise will advertize the latest + /// version supported by the server. /// - public string ProtocolVersion { get; set; } = "2024-11-05"; + public string? ProtocolVersion { get; set; } /// /// Gets or sets a timeout used for the client-server initialization handshake sequence. diff --git a/tests/ModelContextProtocol.TestServer/Program.cs b/tests/ModelContextProtocol.TestServer/Program.cs index 4b9d64de..dbecbf48 100644 --- a/tests/ModelContextProtocol.TestServer/Program.cs +++ b/tests/ModelContextProtocol.TestServer/Program.cs @@ -46,7 +46,6 @@ private static async Task Main(string[] args) Logging = ConfigureLogging(), Completions = ConfigureCompletions(), }, - ProtocolVersion = "2024-11-05", ServerInstructions = "This is a test server with only stub functionality", }; diff --git a/tests/ModelContextProtocol.TestSseServer/Program.cs b/tests/ModelContextProtocol.TestSseServer/Program.cs index 379e34fc..2e37c1d7 100644 --- a/tests/ModelContextProtocol.TestSseServer/Program.cs +++ b/tests/ModelContextProtocol.TestSseServer/Program.cs @@ -34,7 +34,6 @@ private static void ConfigureOptions(McpServerOptions options) Resources = new(), Prompts = new(), }; - options.ProtocolVersion = "2024-11-05"; options.ServerInstructions = "This is a test server with only stub functionality"; Console.WriteLine("Registering handlers.");