Gate 2026-07-28 result fields on negotiated protocol version (#1721) (#1753)

Co-authored-by: Tarek Mahmoud Sayed <tarekms@ntdev.microsoft.com>
Copilot-Session: 80653343-5dcb-43cb-89b0-8ac8e572c4f7
This commit is contained in:
Tarek Mahmoud Sayed 2026-07-27 12:57:50 -07:00 • committed by GitHub
commit 6787c0cc19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -283,7 +283,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<InitializeResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.Equal(expectedAssemblyName.Name, result.ServerInfo.Name);
Assert.Equal(expectedAssemblyName.Version?.ToString() ?? "1.0.0", result.ServerInfo.Version);
Assert.Equal("2024-11-05", result.ProtocolVersion);
@ -386,7 +386,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<InitializeResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.NotNull(result.Capabilities.Extensions);
Assert.True(result.Capabilities.Extensions.ContainsKey("io.myext"));
});
@ -406,7 +406,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<InitializeResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.NotNull(result.Capabilities.Experimental);
Assert.True(result.Capabilities.Experimental.ContainsKey("customFeature"));
});
@ -438,7 +438,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<InitializeResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
// Use reflection to verify every public property on ServerCapabilities is non-null.
// This catches cases where new capability properties are added but not copied
@ -485,7 +485,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<CompleteResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Completion);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.Equal(["test"], result.Completion.Values);
Assert.Equal(2, result.Completion.Total);
Assert.True(result.Completion.HasMore);
@ -529,7 +529,7 @@ public class McpServerTests : LoggedTest
Assert.NotNull(response);
var result = JsonSerializer.Deserialize<CompleteResult>(response.Result, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Completion);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.Equal(["cat"], result.Completion.Values);
Assert.Equal(1, result.Completion.Total);
@ -573,7 +573,7 @@ public class McpServerTests : LoggedTest
Assert.NotNull(response);
var result = JsonSerializer.Deserialize<CompleteResult>(response.Result, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Completion);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.Empty(result.Completion.Values);
await transport.DisposeAsync();
@ -623,7 +623,7 @@ public class McpServerTests : LoggedTest
Assert.NotNull(response);
var result = JsonSerializer.Deserialize<CompleteResult>(response.Result, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Completion);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.Equal(["us-east-1", "us-west-2"], result.Completion.Values);
Assert.Equal(2, result.Completion.Total);
@ -679,7 +679,7 @@ public class McpServerTests : LoggedTest
Assert.NotNull(response);
var result = JsonSerializer.Deserialize<CompleteResult>(response.Result, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Completion);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
// Custom handler values + auto-populated values should be combined
Assert.Equal(["custom-value", "dog", "cat"], result.Completion.Values);
Assert.Equal(3, result.Completion.Total);
@ -727,7 +727,7 @@ public class McpServerTests : LoggedTest
Assert.NotNull(response);
var result = JsonSerializer.Deserialize<CompleteResult>(response.Result, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Completion);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.Equal(["a", "b"], result.Completion.Values);
await transport.DisposeAsync();
@ -766,7 +766,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<ListResourceTemplatesResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.ResourceTemplates);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.NotEmpty(result.ResourceTemplates);
Assert.Equal("test", result.ResourceTemplates[0].UriTemplate);
});
@ -796,7 +796,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<ListResourcesResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Resources);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.NotEmpty(result.Resources);
Assert.Equal("test", result.Resources[0].Uri);
});
@ -832,7 +832,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<ReadResourceResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Contents);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.NotEmpty(result.Contents);
TextResourceContents textResource = Assert.IsType<TextResourceContents>(result.Contents[0]);
@ -870,7 +870,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<ListPromptsResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result?.Prompts);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.NotEmpty(result.Prompts);
Assert.Equal("test", result.Prompts[0].Name);
});
@ -900,7 +900,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<GetPromptResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.Equal("test", result.Description);
});
}
@ -935,7 +935,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<ListToolsResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.NotEmpty(result.Tools);
Assert.Equal("test", result.Tools[0].Name);
});
@ -971,7 +971,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<CallToolResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.NotEmpty(result.Content);
Assert.Equal("test", Assert.IsType<TextContentBlock>(result.Content[0]).Text);
});
@ -1011,6 +1011,31 @@ public class McpServerTests : LoggedTest
});
}
[Fact]
public async Task Can_Handle_SetLoggingLevel_Requests_WithoutHandler_OmitsResultType()
{
// With no custom SetLoggingLevelHandler configured, the server uses its default logging/setLevel
// handler. logging/setLevel is a legacy (<= 2025-11-25) method, so the default handler must also
// serialize its result as an empty object {} without the 2026-07-28 resultType field (issue #1721).
await Can_Handle_Requests(
new ServerCapabilities
{
Logging = new()
},
method: RequestMethods.LoggingSetLevel,
configureOptions: null,
assertResult: (_, response) =>
{
var result = JsonSerializer.Deserialize<EmptyResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Null(result.ResultType);
// The wire response must be exactly {} with no additional properties.
var obj = Assert.IsType<JsonObject>(response);
Assert.Empty(obj);
});
}
[Fact]
public async Task Can_Handle_Call_Tool_Requests_With_McpException()
{
@ -1033,7 +1058,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<CallToolResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.True(result.IsError);
Assert.NotEmpty(result.Content);
var textContent = Assert.IsType<TextContentBlock>(result.Content[0]);
@ -1062,7 +1087,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<CallToolResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.True(result.IsError);
Assert.NotEmpty(result.Content);
var textContent = Assert.IsType<TextContentBlock>(result.Content[0]);
@ -1098,7 +1123,7 @@ public class McpServerTests : LoggedTest
{
var result = JsonSerializer.Deserialize<CallToolResult>(response, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.Equal("complete", result.ResultType);
Assert.Null(result.ResultType);
Assert.True(result.IsError, "Input validation errors should be returned as tool execution errors (IsError=true), not protocol errors");
Assert.NotEmpty(result.Content);
var textContent = Assert.IsType<TextContentBlock>(result.Content[0]);
@ -1359,7 +1384,7 @@ public class McpServerTests : LoggedTest
Assert.NotNull(response.Result);
var initResult = JsonSerializer.Deserialize<InitializeResult>(response.Result, McpJsonUtilities.DefaultOptions);
Assert.NotNull(initResult);
Assert.Equal("complete", initResult.ResultType);
Assert.Null(initResult.ResultType);
Assert.NotNull(initResult.ServerInfo);
await transport.DisposeAsync();