Audit protocol types (#892)

This commit is contained in:
Mackinnon Buck 2025-11-04 18:41:10 -05:00 • committed by GitHub
commit 121929da36
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 262 additions and 168 deletions

View file

@ -204,6 +204,7 @@ public class McpClientExtensionsTests
.Setup(c => c.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(new object(), McpJsonUtilities.DefaultOptions),
});
@ -225,6 +226,7 @@ public class McpClientExtensionsTests
.Setup(c => c.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
});
@ -247,6 +249,7 @@ public class McpClientExtensionsTests
.Setup(c => c.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(callResult, McpJsonUtilities.DefaultOptions),
});
@ -267,6 +270,7 @@ public class McpClientExtensionsTests
.Setup(c => c.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(new EmptyResult(), McpJsonUtilities.DefaultOptions),
});
@ -286,6 +290,7 @@ public class McpClientExtensionsTests
.Setup(c => c.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(new EmptyResult(), McpJsonUtilities.DefaultOptions),
});
@ -308,6 +313,7 @@ public class McpClientExtensionsTests
.Setup(c => c.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
});
@ -330,6 +336,7 @@ public class McpClientExtensionsTests
.Setup(c => c.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
});
@ -352,6 +359,7 @@ public class McpClientExtensionsTests
.Setup(c => c.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
});
@ -374,6 +382,7 @@ public class McpClientExtensionsTests
.Setup(c => c.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
});

View file

@ -17,7 +17,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
mcpServerBuilder.WithReadResourceHandler((request, cancellationToken) =>
new ValueTask<ReadResourceResult>(new ReadResourceResult
{
Contents = [new TextResourceContents { Text = request.Params?.Uri ?? string.Empty }]
Contents = [new TextResourceContents { Text = request.Params?.Uri ?? "", Uri = request.Params?.Uri ?? "" }]
}));
}

View file

@ -70,10 +70,10 @@ public class McpClientTests : ClientServerTestBase
}
[Theory]
[InlineData(null, null)]
[InlineData(null, 10)]
[InlineData(0.7f, 50)]
[InlineData(1.0f, 100)]
public async Task CreateSamplingHandler_ShouldHandleTextMessages(float? temperature, int? maxTokens)
public async Task CreateSamplingHandler_ShouldHandleTextMessages(float? temperature, int maxTokens)
{
// Arrange
var mockChatClient = new Mock<IChatClient>();
@ -472,7 +472,7 @@ public class McpClientTests : ClientServerTestBase
Assert.Equal("TestLogger", m.Logger);
string ? s = JsonSerializer.Deserialize<string>(m.Data.Value, McpJsonUtilities.DefaultOptions);
string? s = JsonSerializer.Deserialize<string>(m.Data.Value, McpJsonUtilities.DefaultOptions);
Assert.NotNull(s);
if (s.Contains("Information"))
@ -505,7 +505,7 @@ public class McpClientTests : ClientServerTestBase
"Error message",
"Information message",
"Warning message",
],
],
data.OrderBy(s => s));
}

View file

@ -66,7 +66,7 @@ public class McpServerBuilderExtensionsFilterTests : ClientServerTestBase
{
return new CallToolResult
{
Content = [new TextContentBlock { Type = "text", Text = $"Error from filter: {ex.Message}" }],
Content = [new TextContentBlock { Text = $"Error from filter: {ex.Message}" }],
IsError = true
};
}

View file

@ -104,7 +104,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
case "test://ResourceTemplate2":
return new ReadResourceResult
{
Contents = [new TextResourceContents { Text = request.Params?.Uri ?? "(null)" }]
Contents = [new TextResourceContents { Text = request.Params?.Uri ?? "(null)", Uri = request.Params?.Uri ?? "(null)" }]
};
}

View file

@ -16,7 +16,7 @@ public class McpServerOptionsSetupTests
.WithListPromptsHandler(async (request, ct) => new ListPromptsResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.NotNull(options.Handlers.ListPromptsHandler);
Assert.NotNull(options.Capabilities?.Prompts);
}
@ -29,7 +29,7 @@ public class McpServerOptionsSetupTests
.WithGetPromptHandler(async (request, ct) => new GetPromptResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.NotNull(options.Handlers.GetPromptHandler);
Assert.NotNull(options.Capabilities?.Prompts);
}
@ -44,7 +44,7 @@ public class McpServerOptionsSetupTests
.WithListResourceTemplatesHandler(async (request, ct) => new ListResourceTemplatesResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.NotNull(options.Handlers.ListResourceTemplatesHandler);
Assert.NotNull(options.Capabilities?.Resources);
}
@ -57,7 +57,7 @@ public class McpServerOptionsSetupTests
.WithListResourcesHandler(async (request, ct) => new ListResourcesResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.NotNull(options.Handlers.ListResourcesHandler);
Assert.NotNull(options.Capabilities?.Resources);
}
@ -70,7 +70,7 @@ public class McpServerOptionsSetupTests
.WithReadResourceHandler(async (request, ct) => new ReadResourceResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.NotNull(options.Handlers.ReadResourceHandler);
Assert.NotNull(options.Capabilities?.Resources);
}
@ -84,7 +84,7 @@ public class McpServerOptionsSetupTests
.WithSubscribeToResourcesHandler(async (request, ct) => new EmptyResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.NotNull(options.Handlers.ListResourcesHandler);
Assert.NotNull(options.Handlers.SubscribeToResourcesHandler);
Assert.NotNull(options.Capabilities?.Resources);
@ -100,7 +100,7 @@ public class McpServerOptionsSetupTests
.WithUnsubscribeFromResourcesHandler(async (request, ct) => new EmptyResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.NotNull(options.Handlers.ListResourcesHandler);
Assert.NotNull(options.Handlers.UnsubscribeFromResourcesHandler);
Assert.NotNull(options.Capabilities?.Resources);
@ -115,7 +115,7 @@ public class McpServerOptionsSetupTests
.WithSubscribeToResourcesHandler(async (request, ct) => new EmptyResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.Null(options.Handlers.SubscribeToResourcesHandler);
Assert.Null(options.Capabilities?.Resources);
}
@ -128,7 +128,7 @@ public class McpServerOptionsSetupTests
.WithUnsubscribeFromResourcesHandler(async (request, ct) => new EmptyResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.Null(options.Handlers.UnsubscribeFromResourcesHandler);
Assert.Null(options.Capabilities?.Resources);
}
@ -233,7 +233,7 @@ public class McpServerOptionsSetupTests
.WithListToolsHandler(async (request, ct) => new ListToolsResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.NotNull(options.Handlers.ListToolsHandler);
Assert.NotNull(options.Capabilities?.Tools);
}
@ -246,7 +246,7 @@ public class McpServerOptionsSetupTests
.WithCallToolHandler(async (request, ct) => new CallToolResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.NotNull(options.Handlers.CallToolHandler);
Assert.NotNull(options.Capabilities?.Tools);
}
@ -261,7 +261,7 @@ public class McpServerOptionsSetupTests
.WithSetLoggingLevelHandler(async (request, ct) => new EmptyResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.NotNull(options.Handlers.SetLoggingLevelHandler);
Assert.NotNull(options.Capabilities?.Logging);
}
@ -276,7 +276,7 @@ public class McpServerOptionsSetupTests
.WithCompleteHandler(async (request, ct) => new CompleteResult());
var options = services.BuildServiceProvider().GetRequiredService<IOptions<McpServerOptions>>().Value;
Assert.NotNull(options.Handlers.CompleteHandler);
Assert.NotNull(options.Capabilities?.Completions);
}

View file

@ -57,6 +57,7 @@ public class McpEndpointExtensionsTests
.Setup(s => s.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(42, McpJsonUtilities.DefaultOptions),
});

View file

@ -16,7 +16,11 @@ public class McpServerExtensionsTests
var server = new Mock<IMcpServer>(MockBehavior.Strict).Object;
var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => await server.SampleAsync(
new CreateMessageRequestParams { Messages = [new SamplingMessage { Role = Role.User, Content = new TextContentBlock { Text = "hi" } }] },
new CreateMessageRequestParams
{
Messages = [new SamplingMessage { Role = Role.User, Content = new TextContentBlock { Text = "hi" } }],
MaxTokens = 1000
},
TestContext.Current.CancellationToken));
Assert.Contains("Prefer using 'McpServer.SampleAsync' instead", ex.Message);
}
@ -90,6 +94,7 @@ public class McpServerExtensionsTests
.Setup(s => s.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
});
@ -97,7 +102,8 @@ public class McpServerExtensionsTests
var result = await server.SampleAsync(new CreateMessageRequestParams
{
Messages = [new SamplingMessage { Role = Role.User, Content = new TextContentBlock { Text = "hi" } }]
Messages = [new SamplingMessage { Role = Role.User, Content = new TextContentBlock { Text = "hi" } }],
MaxTokens = 1000
}, TestContext.Current.CancellationToken);
Assert.Equal("test-model", result.Model);
@ -119,14 +125,28 @@ public class McpServerExtensionsTests
StopReason = "endTurn",
};
const int CustomMaxSamplingOutputTokens = 500;
mockServer
.Setup(s => s.ClientCapabilities)
.Returns(new ClientCapabilities() { Sampling = new() });
mockServer
.Setup(s => s.ServerOptions)
.Returns(new McpServerOptions { MaxSamplingOutputTokens = CustomMaxSamplingOutputTokens });
CreateMessageRequestParams? capturedRequest = null;
mockServer
.Setup(s => s.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.Callback<JsonRpcRequest, CancellationToken>((request, _) =>
{
capturedRequest = JsonSerializer.Deserialize<CreateMessageRequestParams>(
request.Params ?? throw new InvalidOperationException(),
McpJsonUtilities.DefaultOptions);
})
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
});
@ -139,6 +159,10 @@ public class McpServerExtensionsTests
Assert.Equal(ChatRole.Assistant, last.Role);
Assert.Equal("resp", last.Text);
mockServer.Verify(s => s.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()), Times.Once);
// Verify that the default value was used
Assert.NotNull(capturedRequest);
Assert.Equal(CustomMaxSamplingOutputTokens, capturedRequest.MaxTokens);
}
[Fact]
@ -156,6 +180,7 @@ public class McpServerExtensionsTests
.Setup(s => s.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
});
@ -182,6 +207,7 @@ public class McpServerExtensionsTests
.Setup(s => s.SendRequestAsync(It.IsAny<JsonRpcRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new JsonRpcResponse
{
Id = default,
Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
});

View file

@ -538,7 +538,7 @@ public partial class McpServerResourceTests
McpServerResource resource = McpServerResource.Create((McpServer server) =>
{
Assert.Same(mockServer.Object, server);
return new ReadResourceResult { Contents = [new TextResourceContents { Text = "hello" }] };
return new ReadResourceResult { Contents = [new TextResourceContents { Text = "hello", Uri = "" }] };
}, new() { Name = "Test" });
var result = await resource.ReadAsync(
new RequestContext<ReadResourceRequestParams>(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
@ -555,7 +555,7 @@ public partial class McpServerResourceTests
McpServerResource resource = McpServerResource.Create((McpServer server) =>
{
Assert.Same(mockServer.Object, server);
return new TextResourceContents { Text = "hello" };
return new TextResourceContents { Text = "hello", Uri = "" };
}, new() { Name = "Test", SerializerOptions = JsonContext6.Default.Options });
var result = await resource.ReadAsync(
new RequestContext<ReadResourceRequestParams>(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
@ -574,8 +574,8 @@ public partial class McpServerResourceTests
Assert.Same(mockServer.Object, server);
return (IList<ResourceContents>)
[
new TextResourceContents { Text = "hello" },
new BlobResourceContents { Blob = Convert.ToBase64String(new byte[] { 1, 2, 3 }) },
new TextResourceContents { Text = "hello", Uri = "" },
new BlobResourceContents { Blob = Convert.ToBase64String(new byte[] { 1, 2, 3 }), Uri = "" },
];
}, new() { Name = "Test" });
var result = await resource.ReadAsync(

View file

@ -130,7 +130,7 @@ public class McpServerTests : LoggedTest
await using var server = McpServer.Create(transport, _options, LoggerFactory);
SetClientCapabilities(server, new ClientCapabilities());
var action = async () => await server.SampleAsync(new CreateMessageRequestParams { Messages = [] }, CancellationToken.None);
var action = async () => await server.SampleAsync(new CreateMessageRequestParams { Messages = [], MaxTokens = 1000 }, CancellationToken.None);
// Act & Assert
await Assert.ThrowsAsync<InvalidOperationException>(action);
@ -147,7 +147,7 @@ public class McpServerTests : LoggedTest
var runTask = server.RunAsync(TestContext.Current.CancellationToken);
// Act
var result = await server.SampleAsync(new CreateMessageRequestParams { Messages = [] }, CancellationToken.None);
var result = await server.SampleAsync(new CreateMessageRequestParams { Messages = [], MaxTokens = 1000 }, CancellationToken.None);
Assert.NotNull(result);
Assert.NotEmpty(transport.SentMessages);
@ -201,7 +201,7 @@ public class McpServerTests : LoggedTest
SetClientCapabilities(server, new ClientCapabilities());
// Act & Assert
await Assert.ThrowsAsync<InvalidOperationException>(async () => await server.ElicitAsync(new ElicitRequestParams(), CancellationToken.None));
await Assert.ThrowsAsync<InvalidOperationException>(async () => await server.ElicitAsync(new ElicitRequestParams { Message = "" }, CancellationToken.None));
}
[Fact]
@ -214,7 +214,7 @@ public class McpServerTests : LoggedTest
var runTask = server.RunAsync(TestContext.Current.CancellationToken);
// Act
var result = await server.ElicitAsync(new ElicitRequestParams(), CancellationToken.None);
var result = await server.ElicitAsync(new ElicitRequestParams { Message = "" }, CancellationToken.None);
// Assert
Assert.NotNull(result);
@ -377,7 +377,7 @@ public class McpServerTests : LoggedTest
{
return new ReadResourceResult
{
Contents = [new TextResourceContents { Text = "test" }]
Contents = [new TextResourceContents { Text = "test", Uri = "" }]
};
};
options.Handlers.ListResourcesHandler = (request, ct) => throw new NotImplementedException();
@ -659,7 +659,7 @@ public class McpServerTests : LoggedTest
{
Method = method,
Id = new RequestId(55)
}
}
);
var response = await receivedMessage.Task.WaitAsync(TimeSpan.FromSeconds(10));