Tighten draft protocol negotiation boundaries (#1692)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tarek Mahmoud Sayed <tarekms@ntdev.microsoft.com>
This commit is contained in:
Stephen Halter 2026-07-12 14:30:49 -07:00 • committed by GitHub
commit fd38f51e27
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -26,7 +26,7 @@ public class McpServerTests : LoggedTest
{
return new McpServerOptions
{
ProtocolVersion = "2024",
ProtocolVersion = "2024-11-05",
InitializationTimeout = TimeSpan.FromSeconds(30),
Capabilities = capabilities,
};
@ -285,11 +285,92 @@ public class McpServerTests : LoggedTest
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);
Assert.Equal("2024-11-05", result.ProtocolVersion);
Assert.Equal("2024-11-05", server.NegotiatedProtocolVersion);
});
}
[Fact]
public async Task RejectedReservedPerRequestMetadata_DoesNotEstablishProtocolVersion()
{
var ct = TestContext.Current.CancellationToken;
await using var transport = new TestServerTransport();
var options = CreateOptions();
options.ProtocolVersion = null;
await using var server = McpServer.Create(transport, options, LoggerFactory);
var runTask = server.RunAsync(ct);
var rejectedResponse = new TaskCompletionSource<JsonRpcError>(TaskCreationOptions.RunContinuationsAsynchronously);
var acceptedResponse = new TaskCompletionSource<JsonRpcMessage>(TaskCreationOptions.RunContinuationsAsynchronously);
transport.OnMessageSent = message =>
{
if (message is JsonRpcError { Id: var errorId } error && errorId.ToString() == "1")
{
rejectedResponse.TrySetResult(error);
}
else if (message is JsonRpcMessageWithId { Id: var responseId } && responseId.ToString() == "2")
{
acceptedResponse.TrySetResult(message);
}
};
await transport.SendClientMessageAsync(new JsonRpcRequest
{
Id = new RequestId(1),
Method = RequestMethods.ToolsList,
Params = new JsonObject
{
["_meta"] = new JsonObject
{
[MetaKeys.ClientInfo] = new JsonObject
{
["name"] = "test-client",
["version"] = "1.0.0",
},
},
},
Context = new JsonRpcMessageContext
{
ProtocolVersion = McpProtocolVersions.November2025ProtocolVersion,
ClientInfo = new Implementation { Name = "test-client", Version = "1.0.0" },
},
}, ct);
var error = await rejectedResponse.Task.WaitAsync(TestConstants.DefaultTimeout, ct);
Assert.Equal((int)McpErrorCode.InvalidRequest, error.Error.Code);
Assert.Null(server.NegotiatedProtocolVersion);
var clientInfo = new Implementation { Name = "test-client", Version = "1.0.0" };
var clientCapabilities = new ClientCapabilities();
await transport.SendClientMessageAsync(new JsonRpcRequest
{
Id = new RequestId(2),
Method = RequestMethods.ToolsList,
Params = new JsonObject
{
["_meta"] = new JsonObject
{
[MetaKeys.ProtocolVersion] = McpProtocolVersions.July2026ProtocolVersion,
[MetaKeys.ClientInfo] = JsonSerializer.SerializeToNode(clientInfo, McpJsonUtilities.DefaultOptions),
[MetaKeys.ClientCapabilities] = new JsonObject(),
},
},
Context = new JsonRpcMessageContext
{
ProtocolVersion = McpProtocolVersions.July2026ProtocolVersion,
ClientInfo = clientInfo,
ClientCapabilities = clientCapabilities,
},
}, ct);
await acceptedResponse.Task.WaitAsync(TestConstants.DefaultTimeout, ct);
Assert.Equal(McpProtocolVersions.July2026ProtocolVersion, server.NegotiatedProtocolVersion);
await transport.DisposeAsync();
await runTask;
}
[Fact]
public async Task Initialize_IncludesExtensionsInResponse()
{