diff --git a/Directory.Packages.props b/Directory.Packages.props index 0874fa2..67d3e52 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -35,7 +35,7 @@ - - + + \ No newline at end of file diff --git a/ModelContextProtocol.sln b/ModelContextProtocol.sln index 8bd8f62..4b9250c 100644 --- a/ModelContextProtocol.sln +++ b/ModelContextProtocol.sln @@ -31,9 +31,11 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B6FB2B28-D5DE-4654-BE9A-45E305DE4852}" ProjectSection(SolutionItems) = preProject Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props global.json = global.json LICENSE = LICENSE logo.png = logo.png + nuget.config = nuget.config README.MD = README.MD version.json = version.json EndProjectSection diff --git a/tests/ModelContextProtocol.TestServer/Program.cs b/tests/ModelContextProtocol.TestServer/Program.cs index 889b53b..ab28ef3 100644 --- a/tests/ModelContextProtocol.TestServer/Program.cs +++ b/tests/ModelContextProtocol.TestServer/Program.cs @@ -1,5 +1,4 @@ using System.Text; -using System.Text.Json; using ModelContextProtocol.Protocol.Messages; using ModelContextProtocol.Protocol.Transport; using ModelContextProtocol.Protocol.Types; diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientFactoryTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientFactoryTests.cs index bf24770..ce4c038 100644 --- a/tests/ModelContextProtocol.Tests/Client/McpClientFactoryTests.cs +++ b/tests/ModelContextProtocol.Tests/Client/McpClientFactoryTests.cs @@ -17,34 +17,28 @@ public class McpClientFactoryTests [Fact] public async Task CreateAsync_WithInvalidArgs_Throws() { - await Assert.ThrowsAsync("serverConfig", () => McpClientFactory.CreateAsync((McpServerConfig)null!, _defaultOptions)); + await Assert.ThrowsAsync("serverConfig", () => McpClientFactory.CreateAsync((McpServerConfig)null!, _defaultOptions, cancellationToken: TestContext.Current.CancellationToken)); - await Assert.ThrowsAsync("clientOptions", () => McpClientFactory.CreateAsync( - new McpServerConfig() + await Assert.ThrowsAsync("clientOptions", () => McpClientFactory.CreateAsync(new McpServerConfig() { Name = "name", Id = "id", TransportType = TransportTypes.StdIo, - }, (McpClientOptions)null!)); + }, (McpClientOptions)null!, cancellationToken: TestContext.Current.CancellationToken)); - await Assert.ThrowsAsync("serverConfig", () => McpClientFactory.CreateAsync( - new McpServerConfig() + await Assert.ThrowsAsync("serverConfig", () => McpClientFactory.CreateAsync(new McpServerConfig() { Name = "name", Id = "id", TransportType = "somethingunsupported", - }, - _defaultOptions)); + }, _defaultOptions, cancellationToken: TestContext.Current.CancellationToken)); - await Assert.ThrowsAsync(() => McpClientFactory.CreateAsync( - new McpServerConfig() + await Assert.ThrowsAsync(() => McpClientFactory.CreateAsync(new McpServerConfig() { Name = "name", Id = "id", TransportType = TransportTypes.StdIo, - }, - _defaultOptions, - (_, __) => null!)); + }, _defaultOptions, (_, __) => null!, cancellationToken: TestContext.Current.CancellationToken)); } [Fact] @@ -68,7 +62,8 @@ public class McpClientFactoryTests await using var client = await McpClientFactory.CreateAsync( serverConfig, _defaultOptions, - (_, __) => new NopTransport()); + (_, __) => new NopTransport(), + cancellationToken: TestContext.Current.CancellationToken); // Assert Assert.NotNull(client); @@ -91,7 +86,8 @@ public class McpClientFactoryTests await using var client = await McpClientFactory.CreateAsync( serverConfig, _defaultOptions, - (_, __) => new NopTransport()); + (_, __) => new NopTransport(), + cancellationToken: TestContext.Current.CancellationToken); // Assert Assert.NotNull(client); @@ -114,7 +110,8 @@ public class McpClientFactoryTests await using var client = await McpClientFactory.CreateAsync( serverConfig, _defaultOptions, - (_, __) => new NopTransport()); + (_, __) => new NopTransport(), + cancellationToken: TestContext.Current.CancellationToken); // Assert Assert.NotNull(client); @@ -144,7 +141,8 @@ public class McpClientFactoryTests await using var client = await McpClientFactory.CreateAsync( serverConfig, _defaultOptions, - (_, __) => new NopTransport()); + (_, __) => new NopTransport(), + cancellationToken: TestContext.Current.CancellationToken); // Assert Assert.NotNull(client); @@ -171,7 +169,7 @@ public class McpClientFactoryTests }; // act & assert - await Assert.ThrowsAsync(() => McpClientFactory.CreateAsync(config, _defaultOptions)); + await Assert.ThrowsAsync(() => McpClientFactory.CreateAsync(config, _defaultOptions, cancellationToken: TestContext.Current.CancellationToken)); } private sealed class NopTransport : IClientTransport @@ -191,7 +189,7 @@ public class McpClientFactoryTests { switch (message) { - case JsonRpcRequest request: + case JsonRpcRequest: _channel.Writer.TryWrite(new JsonRpcResponse { Id = ((JsonRpcRequest)message).Id, diff --git a/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs b/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs index 4fda215..af13cc7 100644 --- a/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs +++ b/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs @@ -1,11 +1,12 @@ using ModelContextProtocol.Client; -using ModelContextProtocol.Configuration; -using ModelContextProtocol.Protocol.Messages; -using ModelContextProtocol.Protocol.Transport; -using ModelContextProtocol.Protocol.Types; using Microsoft.Extensions.AI; using OpenAI; +using ModelContextProtocol.Protocol.Types; +using ModelContextProtocol.Protocol.Messages; using System.Text.Json; +using ModelContextProtocol.Configuration; +using ModelContextProtocol.Protocol.Transport; +using Xunit.Sdk; namespace ModelContextProtocol.Tests; @@ -61,8 +62,8 @@ public class ClientIntegrationTests : IClassFixture t.Name == "echo"); - var result = await echo.InvokeAsync([new KeyValuePair("message", "Hello MCP!")]); + var result = await echo.InvokeAsync([new KeyValuePair("message", "Hello MCP!")], TestContext.Current.CancellationToken); // assert Assert.NotNull(result); @@ -119,7 +120,7 @@ public class ClientIntegrationTests : IClassFixture 0); @@ -276,17 +277,17 @@ public class ClientIntegrationTests : IClassFixture 0); @@ -340,7 +341,7 @@ public class ClientIntegrationTests : IClassFixture - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs index 76616de..e8ca56b 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs @@ -7,7 +7,6 @@ using ModelContextProtocol.Tests.Utils; using Microsoft.Extensions.AI; using Microsoft.Extensions.Logging; using Moq; -using System.Diagnostics; namespace ModelContextProtocol.Tests.Server; @@ -100,7 +99,7 @@ public class McpServerTests server.GetType().GetField("_isInitializing", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.SetValue(server, true); // Act & Assert - await Assert.ThrowsAsync(() => server.StartAsync()); + await Assert.ThrowsAsync(() => server.StartAsync(TestContext.Current.CancellationToken)); } [Fact] @@ -110,7 +109,7 @@ public class McpServerTests await using var server = new McpServer(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider); server.IsInitialized = true; - await server.StartAsync(); + await server.StartAsync(TestContext.Current.CancellationToken); // Assert _serverTransport.Verify(t => t.StartListeningAsync(It.IsAny()), Times.Never); @@ -123,7 +122,7 @@ public class McpServerTests await using var server = new McpServer(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider); // Act - await server.StartAsync(); + await server.StartAsync(TestContext.Current.CancellationToken); // Assert _serverTransport.Verify(t => t.StartListeningAsync(It.IsAny()), Times.Once); @@ -135,17 +134,16 @@ public class McpServerTests await using var transport = new TestServerTransport(); await using var server = new McpServer(transport, _options, _loggerFactory.Object, _serviceProvider); - await server.StartAsync(); + await server.StartAsync(TestContext.Current.CancellationToken); // Send initialized notification - await transport.SendMessageAsync( - new JsonRpcNotification + await transport.SendMessageAsync(new JsonRpcNotification { Method = "notifications/initialized" } - ); +, TestContext.Current.CancellationToken); - await Task.Delay(50); + await Task.Delay(50, TestContext.Current.CancellationToken); Assert.True(server.IsInitialized); } @@ -171,7 +169,7 @@ public class McpServerTests await using var server = new McpServer(transport, _options, _loggerFactory.Object, _serviceProvider); server.ClientCapabilities = new ClientCapabilities { Sampling = new SamplingCapability() }; - await server.StartAsync(); + await server.StartAsync(TestContext.Current.CancellationToken); // Act var result = await server.RequestSamplingAsync(new CreateMessageRequestParams { Messages = [] }, CancellationToken.None); @@ -200,7 +198,7 @@ public class McpServerTests await using var transport = new TestServerTransport(); await using var server = new McpServer(transport, _options, _loggerFactory.Object, _serviceProvider); server.ClientCapabilities = new ClientCapabilities { Roots = new RootsCapability() }; - await server.StartAsync(); + await server.StartAsync(TestContext.Current.CancellationToken); // Act var result = await server.RequestRootsAsync(new ListRootsRequestParams(), CancellationToken.None); @@ -588,7 +586,7 @@ public class McpServerTests Temperature = 0.75f, MaxOutputTokens = 42, StopSequences = ["."], - }); + }, TestContext.Current.CancellationToken); Assert.Equal("amazingmodel", response.ModelId); Assert.Equal(ChatFinishReason.Stop, response.FinishReason); diff --git a/tests/ModelContextProtocol.Tests/SseIntegrationTests.cs b/tests/ModelContextProtocol.Tests/SseIntegrationTests.cs index 6451e9e..48f91dc 100644 --- a/tests/ModelContextProtocol.Tests/SseIntegrationTests.cs +++ b/tests/ModelContextProtocol.Tests/SseIntegrationTests.cs @@ -37,13 +37,17 @@ public class SseIntegrationTests }; // Act - await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); + await using var client = await McpClientFactory.CreateAsync( + defaultConfig, + defaultOptions, + loggerFactory: loggerFactory, + cancellationToken: TestContext.Current.CancellationToken); // Wait for SSE connection to be established await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10)); // Send a test message through POST endpoint - await client.SendNotificationAsync("test/message", new { message = "Hello, SSE!" }); + await client.SendNotificationAsync("test/message", new { message = "Hello, SSE!" }, TestContext.Current.CancellationToken); // Assert Assert.True(true); @@ -53,10 +57,7 @@ public class SseIntegrationTests [Trait("Execution", "Manual")] public async Task ConnectAndReceiveMessage_EverythingServerWithSse() { - if (!EverythingSseServerFixture.IsDockerAvailable) - { - return; - } + Assert.SkipWhen(!EverythingSseServerFixture.IsDockerAvailable, "docker is not available"); using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole() @@ -82,8 +83,12 @@ public class SseIntegrationTests }; // Create client and run tests - await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); - var tools = await client.ListToolsAsync().ToListAsync(); + await using var client = await McpClientFactory.CreateAsync( + defaultConfig, + defaultOptions, + loggerFactory: loggerFactory, + cancellationToken: TestContext.Current.CancellationToken); + var tools = await client.ListToolsAsync(TestContext.Current.CancellationToken).ToListAsync(TestContext.Current.CancellationToken); // assert Assert.NotEmpty(tools); @@ -93,10 +98,7 @@ public class SseIntegrationTests [Trait("Execution", "Manual")] public async Task Sampling_Sse_EverythingServer() { - if (!EverythingSseServerFixture.IsDockerAvailable) - { - return; - } + Assert.SkipWhen(!EverythingSseServerFixture.IsDockerAvailable, "docker is not available"); // arrange using var loggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => @@ -147,17 +149,19 @@ public class SseIntegrationTests }, }; - await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); + await using var client = await McpClientFactory.CreateAsync( + defaultConfig, + defaultOptions, + loggerFactory: loggerFactory, + cancellationToken: TestContext.Current.CancellationToken); // Call the server's sampleLLM tool which should trigger our sampling handler - var result = await client.CallToolAsync( - "sampleLLM", - new Dictionary + var result = await client.CallToolAsync("sampleLLM", new Dictionary { ["prompt"] = "Test prompt", ["maxTokens"] = 100 } - ); +, TestContext.Current.CancellationToken); // assert Assert.NotNull(result); @@ -194,13 +198,17 @@ public class SseIntegrationTests }; // Act - await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); + await using var client = await McpClientFactory.CreateAsync( + defaultConfig, + defaultOptions, + loggerFactory: loggerFactory, + cancellationToken: TestContext.Current.CancellationToken); // Wait for SSE connection to be established await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10)); // Send a test message through POST endpoint - await client.SendNotificationAsync("test/message", new { message = "Hello, SSE!" }); + await client.SendNotificationAsync("test/message", new { message = "Hello, SSE!" }, TestContext.Current.CancellationToken); // Assert Assert.True(true); @@ -233,7 +241,11 @@ public class SseIntegrationTests }; // Act - await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); + await using var client = await McpClientFactory.CreateAsync( + defaultConfig, + defaultOptions, + loggerFactory: loggerFactory, + cancellationToken: TestContext.Current.CancellationToken); // Wait for SSE connection to be established await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10)); @@ -251,7 +263,7 @@ public class SseIntegrationTests await server.SendTestNotificationAsync("Hello from server!"); // Assert - var message = await receivedNotification.Task.WaitAsync(TimeSpan.FromSeconds(10)); + var message = await receivedNotification.Task.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken); Assert.Equal("Hello from server!", message); } @@ -282,7 +294,11 @@ public class SseIntegrationTests }; // Act - await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); + await using var client = await McpClientFactory.CreateAsync( + defaultConfig, + defaultOptions, + loggerFactory: loggerFactory, + cancellationToken: TestContext.Current.CancellationToken); var mcpClient = (McpClient)client; var transport = (SseClientTransport)mcpClient.Transport; @@ -290,6 +306,6 @@ public class SseIntegrationTests await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10)); // Assert - await Assert.ThrowsAsync(async () => await transport.ConnectAsync()); + await Assert.ThrowsAsync(async () => await transport.ConnectAsync(TestContext.Current.CancellationToken)); } } diff --git a/tests/ModelContextProtocol.Tests/SseServerIntegrationTests.cs b/tests/ModelContextProtocol.Tests/SseServerIntegrationTests.cs index 186fee2..97f9348 100644 --- a/tests/ModelContextProtocol.Tests/SseServerIntegrationTests.cs +++ b/tests/ModelContextProtocol.Tests/SseServerIntegrationTests.cs @@ -53,7 +53,7 @@ public class SseServerIntegrationTests : IClassFixture + var result = await client.CallToolAsync("sampleLLM", new Dictionary { ["prompt"] = "Test prompt", ["maxTokens"] = 100 - } - ); + }, + TestContext.Current.CancellationToken); // assert Assert.NotNull(result); diff --git a/tests/ModelContextProtocol.Tests/Transport/SseClientTransportTests.cs b/tests/ModelContextProtocol.Tests/Transport/SseClientTransportTests.cs index 67fd84c..26aaecd 100644 --- a/tests/ModelContextProtocol.Tests/Transport/SseClientTransportTests.cs +++ b/tests/ModelContextProtocol.Tests/Transport/SseClientTransportTests.cs @@ -95,7 +95,7 @@ public class SseClientTransportTests }; }; - await transport.ConnectAsync(); + await transport.ConnectAsync(TestContext.Current.CancellationToken); } [Fact] @@ -174,7 +174,7 @@ public class SseClientTransportTests } }; - await transport.ConnectAsync(); + await transport.ConnectAsync(TestContext.Current.CancellationToken); await transport.SendMessageAsync(new JsonRpcRequest() { Method = "initialize", Id = RequestId.FromNumber(44) }, CancellationToken.None); Assert.True(true); @@ -213,7 +213,7 @@ public class SseClientTransportTests } }; - await transport.ConnectAsync(); + await transport.ConnectAsync(TestContext.Current.CancellationToken); await transport.SendMessageAsync(new JsonRpcRequest() { Method = "initialize", Id = RequestId.FromNumber(44) }, CancellationToken.None); Assert.True(true); @@ -251,7 +251,7 @@ public class SseClientTransportTests throw new IOException("Abort"); }; - await transport.ConnectAsync(); + await transport.ConnectAsync(TestContext.Current.CancellationToken); Assert.True(transport.MessageReader.TryRead(out var message)); Assert.NotNull(message); Assert.IsType(message); diff --git a/tests/ModelContextProtocol.Tests/Transport/StdioServerTransportTests.cs b/tests/ModelContextProtocol.Tests/Transport/StdioServerTransportTests.cs index 76efb6d..a150650 100644 --- a/tests/ModelContextProtocol.Tests/Transport/StdioServerTransportTests.cs +++ b/tests/ModelContextProtocol.Tests/Transport/StdioServerTransportTests.cs @@ -52,7 +52,7 @@ public class StdioServerTransportTests { await using var transport = new StdioServerTransport(_serverOptions); - await transport.StartListeningAsync(); + await transport.StartListeningAsync(TestContext.Current.CancellationToken); Assert.True(transport.IsConnected); } @@ -70,12 +70,12 @@ public class StdioServerTransportTests Console.SetOut(output); await using var transport = new StdioServerTransport(_serverOptions, NullLoggerFactory.Instance); - await transport.StartListeningAsync(); + await transport.StartListeningAsync(TestContext.Current.CancellationToken); var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) }; - await transport.SendMessageAsync(message); + await transport.SendMessageAsync(message, TestContext.Current.CancellationToken); var result = output.ToString()?.Trim(); var expected = JsonSerializer.Serialize(message, McpJsonUtilities.DefaultOptions); @@ -96,7 +96,7 @@ public class StdioServerTransportTests var message = new JsonRpcRequest { Method = "test" }; - await Assert.ThrowsAsync(() => transport.SendMessageAsync(message)); + await Assert.ThrowsAsync(() => transport.SendMessageAsync(message, TestContext.Current.CancellationToken)); } [Fact] @@ -123,9 +123,9 @@ public class StdioServerTransportTests Console.SetOut(new StringWriter()); await using var transport = new StdioServerTransport(_serverOptions); - await transport.StartListeningAsync(); + await transport.StartListeningAsync(TestContext.Current.CancellationToken); - var canRead = await transport.MessageReader.WaitToReadAsync(); + var canRead = await transport.MessageReader.WaitToReadAsync(TestContext.Current.CancellationToken); Assert.True(canRead, "Nothing to read here from transport message reader"); Assert.True(transport.MessageReader.TryPeek(out var readMessage)); @@ -144,7 +144,7 @@ public class StdioServerTransportTests public async Task CleanupAsync_Should_Cleanup_Resources() { var transport = new StdioServerTransport(_serverOptions); - await transport.StartListeningAsync(); + await transport.StartListeningAsync(TestContext.Current.CancellationToken); await transport.DisposeAsync();