About mcp-csharp-test
A skill for testing C# MCP servers, covering unit tests for tool methods and integration tests through the MCP protocol. It also outlines LLM evaluations and CI test setup.
Key features
- 01Unit-test individual tool methods
- 02Mock tool dependencies
- 03Test MCP listing and invocation
- 04Run filtered tests and coverage
- 05Design read-only LLM evaluations
Use cases
- 01Add automated tests to a server
- 02Verify tool behavior with mocks
- 03Validate protocol-level tool calls
- 04Set up CI testing
MCP support
Describes integration testing with an MCP client, including verifying tool listings and invoking tools through the protocol.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ dotnet/ skills --skill mcp-csharp-testAfter installing, type/mcp-csharp-testin Claude Code to confirm it loaded.
Bundled filesreferences/evaluations.mdreferences/test-patterns.md
Details
From the project READMEC# MCP Server Testing
Test MCP servers at two levels: unit tests for individual tool methods, and integration tests that exercise the full MCP protocol in-memory.
When to Use
- Adding automated tests to an MCP server
- Testing individual tool methods with mocked dependencies
- Writing integration tests that validate tool listing and invocation via MCP protocol
- Setting up CI test pipelines for MCP servers
Stop Signals
- No server yet? → Use
mcp-csharp-createfirst
Inputs
| Input | Required | Description |
|---|---|---|
| MCP server project path | Yes | Path to the server .csproj being tested |
| Test framework | Recommended | Default: xUnit. Also supports NUnit or MSTest |
| Transport type | Recommended | Determines integration test approach (stdio vs HTTP) |
Workflow
Step 1: Create the test project
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 FluentAssertions
Step 2: Write unit tests for tool methods
Test tool methods directly — fastest and most isolated:
public 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:");
}
}
For tools with DI dependencies, mock the dependency:
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");
}
}
Step 3: Write integration tests with MCP client
Test the full MCP protocol using a client-server connection:
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");
}
}
For the SDK's ClientServerTestBase (in-memory testing) and HTTP testing with WebApplicationFactory, see references/test-patterns.md.
Step 4: Run tests
# Run all tests
dotnet test
# Run a specific test class
dotnet test --filter "FullyQualifiedName~MyToolTests"
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"
Step 5: Write evaluations
Evaluations measure how well an LLM uses your tools. Good evaluation questions should be:
- Read-only and non-destructive — never modify data as a side effect
- Deterministic — have a single verifiable correct answer
- Multi-step — require the LLM to call multiple tools or reason across results
For the evaluation format, example questions, and detailed guidance, see references/evaluations.md.
Validation
- Unit tests cover all tool methods, including edge cases
- Integration tests verify tool listing via
ListToolsAsync() - Integration tests verify tool invocation via
CallToolAsync() - All tests pass:
dotnet test - Tests run in CI without manual setup
Common Pitfalls
| Pitfall | Solution |
|---|---|
Integration test hangs on CreateAsync | Server fails to start. Verify dotnet build succeeds first. For stdio, ensure no stdout logging |
StdioClientTransport not finding project | Use the correct relative path to .csproj from the test project directory |
| Tests pass locally but fail in CI | Run dotnet build before test execution. Use --no-build only after an explicit build step |
Mocking HttpClient is awkward | Mock HttpMessageHandler, not HttpClient directly. See references/test-patterns.md |
| Full test suite runs are slow | Use --filter for development. Run the full suite only for CI verification |
Related Skills
mcp-csharp-create— Create a new MCP server projectmcp-csharp-publish— NuGet, Docker, Azure deployment
Reference Files
- references/test-patterns.md — Complete test code examples:
ClientServerTestBasein-memory pattern,WebApplicationFactoryfor HTTP,MockHttpMessageHandlerhelper, test categorization, coverage reporting. Load when: writing integration tests or need detailed mock patterns. - references/evaluations.md — Evaluation format, question design principles, and example eval questions. Load when: user asks about evaluations, eval questions, or measuring tool quality.
More Info
- xUnit documentation — Getting started with xUnit for .NET