Skip to content

Improve protocol version handling #468

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 3, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/ModelContextProtocol.Core/Client/McpClient.cs
Original file line number Diff line number Diff line change
@@ -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
13 changes: 10 additions & 3 deletions src/ModelContextProtocol.Core/Client/McpClientOptions.cs
Original file line number Diff line number Diff line change
@@ -34,11 +34,18 @@ public class McpClientOptions
/// Gets or sets the protocol version to request from the server, using a date-based versioning scheme.
/// </summary>
/// <remarks>
/// <para>
/// 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.
/// </para>
/// <para>
/// If non-<see langword="null"/>, 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 <see langword="null"/>, the client will request the latest version supported by the server
/// but will allow any supported version that the server advertizes in its response.
/// </para>
/// </remarks>
public string ProtocolVersion { get; set; } = "2024-11-05";
public string? ProtocolVersion { get; set; }

/// <summary>
/// Gets or sets a timeout for the client-server initialization handshake sequence.
10 changes: 10 additions & 0 deletions src/ModelContextProtocol.Core/McpSession.cs
Original file line number Diff line number Diff line change
@@ -28,6 +28,16 @@ internal sealed partial class McpSession : IDisposable
private static readonly Histogram<double> s_serverOperationDuration = Diagnostics.CreateDurationHistogram(
"mcp.server.operation.duration", "Measures the duration of inbound message processing.", longBuckets: false);

/// <summary>The latest version of the protocol supported by this implementation.</summary>
internal const string LatestProtocolVersion = "2025-03-26";

/// <summary>All protocol versions supported by this implementation.</summary>
internal static readonly string[] SupportedProtocolVersions =
[
"2024-11-05",
LatestProtocolVersion,
];

private readonly bool _isServer;
private readonly string _transportKind;
private readonly ITransport _transport;
2 changes: 1 addition & 1 deletion src/ModelContextProtocol.Core/Protocol/PaginatedRequest.cs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ namespace ModelContextProtocol.Protocol;
/// Provides a base class for paginated requests.
/// </summary>
/// <remarks>
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">See the schema for details</see>
/// </remarks>
public class PaginatedRequestParams : RequestParams
{
13 changes: 12 additions & 1 deletion src/ModelContextProtocol.Core/Server/McpServer.cs
Original file line number Diff line number Diff line change
@@ -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(),
5 changes: 4 additions & 1 deletion src/ModelContextProtocol.Core/Server/McpServerOptions.cs
Original file line number Diff line number Diff line change
@@ -32,8 +32,11 @@ public class McpServerOptions
/// <remarks>
/// 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 <see langword="null"/>, 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.
/// </remarks>
public string ProtocolVersion { get; set; } = "2024-11-05";
public string? ProtocolVersion { get; set; }

/// <summary>
/// Gets or sets a timeout used for the client-server initialization handshake sequence.
1 change: 0 additions & 1 deletion tests/ModelContextProtocol.TestServer/Program.cs
Original file line number Diff line number Diff line change
@@ -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",
};

1 change: 0 additions & 1 deletion tests/ModelContextProtocol.TestSseServer/Program.cs
Original file line number Diff line number Diff line change
@@ -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.");