dotnet-test-coverage — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited dotnet-test-coverage (Plugin) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<!-- mcp-name: io.github.Hyeonu-Cha/dotnet-coverage-mcp -->
An MCP (Model Context Protocol) server that gives AI assistants — Claude Code, Gemini CLI, and others — direct access to .NET test-coverage tooling. Run dotnet test, parse Cobertura XML, identify uncovered branches, diff coverage between runs, and append test code — all over stdio.
This server lets an AI assistant run unit tests, collect coverage data, and analyse results — all without leaving the chat. Instead of manually running dotnet test and parsing reports, the AI can call the server's tools directly to:
The server starts as a console process and communicates over stdio using the MCP protocol. An MCP-compatible client (Claude Code, Gemini CLI, etc.) launches the process and calls its tools as if they were functions.
AI Client <--stdio/MCP--> dotnet-coverage-mcp <--shell--> dotnet test + reportgenerator| Tool | Description |
|---|---|
GetSourceFiles | Discover .cs files from a file, folder, or .csproj project. Returns file metadata (lines, method count) and smart batches grouped by lineBudget. |
RunTestsWithCoverage | Run dotnet test with XPlat Code Coverage, generate a JSON summary via reportgenerator. Returns paths to Summary.json and coverage.cobertura.xml. Supports forceRestore and sessionId for concurrent isolation. |
GetCoverageSummary | Parse Summary.json into structured class/method coverage data sorted by branch coverage ascending (lowest first). |
GetFileCoverage | Get coverage for a single source file from Cobertura XML. Returns allMeetTarget (true when all classes meet the configured targetRate for both line and branch coverage; default 0.8). Supports sessionId. |
GetUncoveredBranches | Find uncovered branch conditions for methods matching a given name. Returns all matching methods with partial name support. Supports sessionId. |
GetCoverageDiff | Compare current Cobertura XML against baseline. Shows method-level changes including new and removed methods. Supports sessionId for concurrent isolation. |
AppendTestCode | Insert or append C# test code into a test file. Supports anchor-based insertion with whitespace-tolerant fallback matching. Uses atomic writes to prevent file corruption. |
CleanupSession | Remove session state files and TestResults/coveragereport directories. Pass sessionId to scope, or omit to clean artifacts older than maxAgeMinutes (default 120). |
For projects with many source files, the recommended workflow is:
GetSourceFiles on a folder or .csproj to get all files and smart batchesRunTestsWithCoverage with a broad filter (e.g., *) to collect coverage across all filesGetFileCoverage for each file in the current batch (instant XML parsing, no test re-run)GetUncoveredBranches for eachAppendTestCode to add test methodsGetCoverageDiff to verify improvementThis minimises dotnet test invocations (the main bottleneck) while still tracking per-file progress.
Multiple AI agents can safely run in parallel against the same repository by passing a sessionId to each tool call:
RunTestsWithCoverage creates TestResults-{hash}/ and coveragereport-{hash}/ per session, preventing one agent from deleting another's XML mid-parse.mcp-coverage/.coverage-state-{hash}, so ResolveCoberturaPath resolves to the correct XML for each sessionGetCoverageDiff stores baselines as .coverage-prev-{hash}.xml per sessionWithout sessionId, tools use shared defaults — safe for single-agent use.
pathNotAllowed. When unset, the server logs a warning once and accepts any path (backward-compatible, but not recommended for shared environments). export COVERAGE_MCP_ALLOWED_ROOT=/path/to/your/repoInstall the server as a global .NET tool from NuGet:
dotnet tool install --global dotnet-coverage-mcpThe server depends on the reportgenerator global tool to render coverage reports — install it too:
dotnet tool install --global dotnet-reportgenerator-globaltoolAfter install, the dotnet-coverage-mcp command is on your PATH.
cd <path-to-dotnet-coverage-mcp>
# Restore dependencies
dotnet restore
# Build
dotnet build
# Run
dotnet runThe server will start and wait for MCP messages over stdin/stdout.
After installing the global tool (dotnet tool install --global dotnet-coverage-mcp), register the server with your MCP client. Set COVERAGE_MCP_ALLOWED_ROOT to the repository you want the server to operate on.
claude mcp add coverage --env COVERAGE_MCP_ALLOWED_ROOT=/path/to/your/repo -- dotnet-coverage-mcpAdd to claude_desktop_config.json (Settings → Developer → Edit Config):
{
"mcpServers": {
"coverage": {
"command": "dotnet-coverage-mcp",
"env": {
"COVERAGE_MCP_ALLOWED_ROOT": "/path/to/your/repo"
}
}
}
}Add to ~/.cursor/mcp.json (global) or .cursor/mcp.json (per-project):
{
"mcpServers": {
"coverage": {
"command": "dotnet-coverage-mcp",
"env": {
"COVERAGE_MCP_ALLOWED_ROOT": "/path/to/your/repo"
}
}
}
}Add to .vscode/mcp.json:
{
"servers": {
"coverage": {
"type": "stdio",
"command": "dotnet-coverage-mcp",
"env": {
"COVERAGE_MCP_ALLOWED_ROOT": "/path/to/your/repo"
}
}
}
}To run from source instead of the global tool, use dotnet run:
{
"mcpServers": {
"coverage": {
"command": "dotnet",
"args": ["run", "--project", "<path-to-dotnet-coverage-mcp>"],
"transport": "stdio"
}
}
}Or point directly at the compiled executable:
{
"mcpServers": {
"coverage": {
"command": "<path-to-dotnet-coverage-mcp>\\bin\\Debug\\net9.0\\DotNetCoverageMcp.exe",
"transport": "stdio"
}
}
}GetSourceFiles| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Path to a .cs file, folder, or .csproj project |
lineBudget | int | No | Max total lines per batch (default: 300). Small files are grouped together; large files get their own batch. |
RunTestsWithCoverage| Parameter | Type | Required | Description |
|---|---|---|---|
testProjectPath | string | Yes | Full path to the .csproj test project |
filter | string | Yes | Test filter string (matched against FullyQualifiedName). Use * or , for broad runs across multiple test classes. |
workingDir | string | No | Working directory; defaults to the project directory |
forceRestore | bool | No | When true, skips the --no-restore flag. Use after scaffolding a new test project or adding NuGet packages. |
sessionId | string | No | Isolates output directories (TestResults-{hash}/, coveragereport-{hash}/) and state files for concurrent multi-agent use. |
includeClass | string | No | Restrict coverage collection to types matching this name. Forwarded to coverlet as /p:Include=[*]*{includeClass}. Always honored when set, independent of filter — pass an explicit value to scope coverage; omit it to collect coverage for everything the run touches. |
GetCoverageSummary| Parameter | Type | Required | Description |
|---|---|---|---|
summaryJsonPath | string | Yes | Full path to the generated Summary.json file |
GetFileCoverage| Parameter | Type | Required | Description |
|---|---|---|---|
coberturaXmlPath | string | Yes | Path to coverage.cobertura.xml (falls back to .mcp-coverage/.coverage-state if not found) |
sourceFileName | string | Yes | Source file name to look up (e.g., ExampleService.cs) |
sessionId | string | No | Resolves session-scoped state file for concurrent isolation. |
targetRate | double | No | Coverage threshold (0.0–1.0) used to compute allMeetTarget. Default 0.8. |
GetUncoveredBranches| Parameter | Type | Required | Description |
|---|---|---|---|
coberturaXmlPath | string | Yes | Path to coverage.cobertura.xml (falls back to .mcp-coverage/.coverage-state if not found) |
methodName | string | Yes | Method name to inspect (partial match supported; returns all matching methods) |
sessionId | string | No | Resolves session-scoped state file for concurrent isolation. |
GetCoverageDiff| Parameter | Type | Required | Description |
|---|---|---|---|
coberturaXmlPath | string | Yes | Path to the current coverage.cobertura.xml |
workingDir | string | No | Directory for storing baseline; defaults to the XML's parent directory |
sessionId | string | No | Isolates baseline as .coverage-prev-{hash}.xml and resolves session-scoped state file. |
AppendTestCode| Parameter | Type | Required | Description |
|---|---|---|---|
testFilePath | string | Yes | Full path to the target .cs test file |
codeToAppend | string | Yes | C# code to insert |
insertAfterAnchor | string | No | If provided, inserts code after the last occurrence of this string (with whitespace-tolerant fallback). If omitted, appends before the last }. |
CleanupSession| Parameter | Type | Required | Description |
|---|---|---|---|
workingDir | string | Yes | Project working directory containing .mcp-coverage/ and TestResults artifacts |
sessionId | string | No | When set, removes only state files and directories scoped to this session. |
maxAgeMinutes | int | No | When sessionId is omitted, removes artifacts older than this many minutes. Default 120. |
All state files are written to a .mcp-coverage/ subdirectory inside the working directory, keeping the project root clean. Add .mcp-coverage/ to the target repository's .gitignore.
| File | Purpose |
|---|---|
.coverage-state | Default Cobertura XML path for single-agent use |
.coverage-state-{hash} | Session-scoped Cobertura XML path |
.coverage-prev.xml | Default coverage baseline for diff |
.coverage-prev-{hash}.xml | Session-scoped coverage baseline |
This repo includes a plugin/ directory with Claude Code skills and an agent definition for guided test coverage workflows:
plugin/
├── plugin.json
├── agents/
│ └── test-coverage.agent.md
└── skills/
├── scaffold-test-files/ — Create test directories and files mirroring source structure
├── run-coverage/ — Run tests and view coverage reports
├── analyze-coverage-gaps/ — Find uncovered branches and compare diffs
└── improve-test-coverage/ — Iterative loop to reach 80% coverageThe skills support NUnit, xUnit, and MSTest with framework-agnostic reference docs in references/unit.md and references/integration.md.
| Package | Version | Purpose |
|---|---|---|
Microsoft.Extensions.Hosting | 10.0.7 | DI and hosting |
ModelContextProtocol | 1.2.0 | MCP server framework |
Microsoft.CodeAnalysis.CSharp | 5.3.0 | Roslyn AST for safe code insertion and accurate method counting (~15MB) |
dotnet-coverage-mcp runs as a local stdio process and validates every tool argument against COVERAGE_MCP_ALLOWED_ROOT to confine filesystem access. See SECURITY.md for the threat model, hardening recommendations, and how to report a vulnerability.
Contributions are welcome. See CONTRIBUTING.md for development setup, pull request guidelines, and code conventions. Notable changes are tracked in CHANGELOG.md.
Maintainer-only — release process, NuGet publishing, and MCP registry submission are documented in RELEASING.md.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.