Loading...
Loading...
Test C# MCP servers at multiple levels: unit tests for individual tools and integration tests using the MCP client SDK. USE FOR: unit testing MCP tool methods, integration testing with in-memory MCP client/server, end-to-end testing via MCP protocol, testing HTTP MCP servers with WebApplicationFactory, mocking dependencies in tool tests, creating evaluations for MCP servers, writing eval questions, measuring tool quality. DO NOT USE FOR: testing MCP clients (this is server testing only), load or performance testing, testing non-.NET MCP servers, debugging server issues (use mcp-csharp-debug).
npx skill4agent add dotnet/skills mcp-csharp-testmcp-csharp-createmcp-csharp-debugmcp-csharp-debug| Input | Required | Description |
|---|---|---|
| MCP server project path | Yes | Path to the server |
| Test framework | Recommended | Default: xUnit. Also supports NUnit or MSTest |
| Transport type | Recommended | Determines integration test approach (stdio vs HTTP) |
dotnet new xunit -n <ServerName>.Tests
cd <ServerName>.Tests
dotnet add reference ../<ServerName>/<ServerName>.csproj
dotnet add package ModelContextProtocol
dotnet add package Moq
dotnet add package FluentAssertionspublic class MyToolTests
{
[Fact]
public void Echo_ReturnsFormattedMessage()
{
var result = MyTools.Echo("Hello");
result.Should().Be("Echo: Hello");
}
[Theory]
[InlineData("")]
[InlineData(" ")]
public void Echo_HandlesEdgeCases(string input)
{
var result = MyTools.Echo(input);
result.Should().StartWith("Echo:");
}
}public class ApiToolTests
{
[Fact]
public async Task FetchData_ReturnsApiResponse()
{
var handler = new MockHttpMessageHandler("""{"id": 1}""");
var httpClient = new HttpClient(handler);
var result = await ApiTools.FetchData(httpClient, "resource-1");
result.Should().Contain("id");
}
}using ModelContextProtocol.Client;
public class ServerIntegrationTests : IAsyncLifetime
{
private McpClient _client = null!;
public async Task InitializeAsync()
{
var transport = new StdioClientTransport(new StdioClientTransportOptions
{
Name = "TestClient",
Command = "dotnet",
Arguments = ["run", "--project", "../<ServerName>/<ServerName>.csproj"]
});
_client = await McpClient.CreateAsync(transport);
}
public async Task DisposeAsync() => await _client.DisposeAsync();
[Fact]
public async Task Server_ListsExpectedTools()
{
var tools = await _client.ListToolsAsync();
tools.Should().Contain(t => t.Name == "echo");
}
[Fact]
public async Task Tool_ReturnsExpectedResult()
{
var result = await _client.CallToolAsync("echo",
new Dictionary<string, object?> { ["message"] = "Test" });
var text = result.Content.OfType<TextContentBlock>().First().Text;
text.Should().Contain("Test");
}
}ClientServerTestBaseWebApplicationFactory# Run all tests
dotnet test
# Run a specific test class
dotnet test --filter "FullyQualifiedName~MyToolTests"
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"ListToolsAsync()CallToolAsync()dotnet test| Pitfall | Solution |
|---|---|
Integration test hangs on | Server fails to start. Verify |
| Use the correct relative path to |
| Tests pass locally but fail in CI | Run |
Mocking | Mock |
| Full test suite runs are slow | Use |
mcp-csharp-createmcp-csharp-debugmcp-csharp-publishClientServerTestBaseWebApplicationFactoryMockHttpMessageHandler