{
  "markdown": "# dotnet-coverage-mcp\n\n<!-- mcp-name: io.github.Hyeonu-Cha/dotnet-coverage-mcp -->\n\n[![build](https://github.com/Hyeonu-Cha/dotnet-coverage-mcp/actions/workflows/dotnet.yml/badge.svg)](https://github.com/Hyeonu-Cha/dotnet-coverage-mcp/actions/workflows/dotnet.yml)\n[![tests](https://img.shields.io/github/actions/workflow/status/Hyeonu-Cha/dotnet-coverage-mcp/dotnet.yml?branch=main&label=tests)](https://github.com/Hyeonu-Cha/dotnet-coverage-mcp/actions/workflows/dotnet.yml)\n[![NuGet](https://img.shields.io/nuget/v/dotnet-coverage-mcp.svg)](https://www.nuget.org/packages/dotnet-coverage-mcp/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nAn 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.\n\n## Purpose\n\nThis 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:\n\n- Discover source files and build smart batches by line budget\n- Run a filtered set of tests and collect coverage\n- Read compact, AI-optimised coverage summaries (method-level line/branch rates)\n- Check per-file coverage against a configurable target rate (default 80%)\n- Identify uncovered branches as structured JSON\n- Diff coverage between runs to see only what changed\n- Append new test code to an existing test file with atomic writes\n\n## How It Works\n\nThe 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.\n\n```\nAI Client  <--stdio/MCP-->  dotnet-coverage-mcp  <--shell-->  dotnet test + reportgenerator\n```\n\n## Available Tools\n\n| Tool | Description |\n|------|-------------|\n| `GetSourceFiles` | Discover `.cs` files from a file, folder, or `.csproj` project. Returns file metadata (lines, method count) and smart batches grouped by `lineBudget`. |\n| `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. |\n| `GetCoverageSummary` | Parse `Summary.json` into structured class/method coverage data sorted worst-first by branch coverage. Optional `belowTarget`/`topN`/`methodsPerClass` filters trim the response to what still needs work. |\n| `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`. |\n| `GetUncoveredBranches` | Find uncovered branch conditions for methods matching a given name. Returns all matching methods with partial name support. Supports `sessionId`. |\n| `GetCoverageDiff` | Compare current Cobertura XML against baseline. Shows method-level changes including new and removed methods. Supports `sessionId` for concurrent isolation. |\n| `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. |\n| `CleanupSession` | Remove session state files and `TestResults/coveragereport` directories. Pass `sessionId` to scope, or omit to clean artifacts older than `maxAgeMinutes` (default 120). |\n\n## Batch Workflow\n\nFor projects with many source files, the recommended workflow is:\n\n1. **Discover** — Call `GetSourceFiles` on a folder or `.csproj` to get all files and smart batches\n2. **Run once** — Call `RunTestsWithCoverage` with a broad filter (e.g., `*`) to collect coverage across all files\n3. **Check per-file** — Call `GetFileCoverage` for each file in the current batch (instant XML parsing, no test re-run)\n4. **Focus** — Pick the 3 lowest branch-coverage methods and call `GetUncoveredBranches` for each\n5. **Write tests** — Use `AppendTestCode` to add test methods\n6. **Re-run and diff** — Run tests once, call `GetCoverageDiff` to verify improvement\n7. **Repeat** — Continue until batch files meet the target rate (default 80%) or 3 cycles with no improvement, then move to next batch\n\nThis minimises `dotnet test` invocations (the main bottleneck) while still tracking per-file progress.\n\n## Concurrency\n\nMultiple AI agents can run in parallel by passing a `sessionId` to each tool call, which isolates their coverage artifacts:\n\n- **Isolated output directories** — `RunTestsWithCoverage` creates `TestResults-{hash}/` and `coveragereport-{hash}/` per session, preventing one agent from deleting another's XML mid-parse\n- **Scoped state files** — Coverage state is written to `.mcp-coverage/.coverage-state-{hash}`, so `ResolveCoberturaPath` resolves to the correct XML for each session\n- **Scoped baselines** — `GetCoverageDiff` stores baselines as `.coverage-prev-{hash}.xml` per session\n- **Atomic writes** — All file writes (state files and test code) use write-to-temp-then-rename to prevent corruption from race conditions or process crashes\n\n> **Limitation — build outputs are not session-scoped.** `sessionId` isolates coverage *artifacts*, not the .NET *build*. `dotnet test` compiles the target project into its shared `obj/` and `bin/`, which are not per-session, so two agents running `RunTestsWithCoverage` against the **same** test project at the same time collide on those outputs and fail with `buildError` (e.g. `CS2012: the file is being used by another process`). Run parallel agents against **different** test projects, or on separate working copies of the repo. Multiple agents on one project are fine as long as their `dotnet test` builds don't overlap.\n\nWithout `sessionId`, tools use shared defaults — safe for single-agent use.\n\n## Requirements\n\n- **.NET 9.0 SDK (or later)** — [https://dotnet.microsoft.com/download](https://dotnet.microsoft.com/download)\n- **reportgenerator** global tool — the server shells out to it to render coverage reports (installed in the [Install](#install) step below)\n- An MCP-compatible client (Claude Code, Gemini CLI, etc.)\n- **`COVERAGE_MCP_ALLOWED_ROOT`** — recommended. Set to your repository root to restrict every tool's filesystem access to that subtree. Any path passed by the client outside this root is rejected with `pathNotAllowed`. When unset, the server logs a warning once and accepts any path (backward-compatible, but not recommended for shared environments).\n\n  ```bash\n  export COVERAGE_MCP_ALLOWED_ROOT=/path/to/your/repo\n  ```\n\n## Install\n\nInstall the server as a global .NET tool from NuGet:\n\n```bash\ndotnet tool install --global dotnet-coverage-mcp\n```\n\nThe server depends on the **reportgenerator** global tool to render coverage reports — install it too:\n\n```bash\ndotnet tool install --global dotnet-reportgenerator-globaltool\n```\n\nAfter install, the `dotnet-coverage-mcp` command is on your PATH.\n\n## Build & Run (from source)\n\n```bash\ncd <path-to-dotnet-coverage-mcp>\n\n# Restore dependencies\ndotnet restore\n\n# Build\ndotnet build\n\n# Run\ndotnet run\n```\n\nThe server will start and wait for MCP messages over stdin/stdout.\n\n## MCP Client Configuration\n\nAfter installing the global tool (`dotnet tool install --global dotnet-coverage-mcp`),\nregister the server with your MCP client. Set `COVERAGE_MCP_ALLOWED_ROOT` to the\nrepository you want the server to operate on.\n\n### Claude Code\n\n```bash\nclaude mcp add coverage --env COVERAGE_MCP_ALLOWED_ROOT=/path/to/your/repo -- dotnet-coverage-mcp\n```\n\n### Claude Desktop\n\nAdd to `claude_desktop_config.json` (Settings → Developer → Edit Config):\n\n```json\n{\n  \"mcpServers\": {\n    \"coverage\": {\n      \"command\": \"dotnet-coverage-mcp\",\n      \"env\": {\n        \"COVERAGE_MCP_ALLOWED_ROOT\": \"/path/to/your/repo\"\n      }\n    }\n  }\n}\n```\n\n### Cursor\n\nAdd to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per-project):\n\n```json\n{\n  \"mcpServers\": {\n    \"coverage\": {\n      \"command\": \"dotnet-coverage-mcp\",\n      \"env\": {\n        \"COVERAGE_MCP_ALLOWED_ROOT\": \"/path/to/your/repo\"\n      }\n    }\n  }\n}\n```\n\n### VS Code (GitHub Copilot)\n\nAdd to `.vscode/mcp.json`:\n\n```json\n{\n  \"servers\": {\n    \"coverage\": {\n      \"type\": \"stdio\",\n      \"command\": \"dotnet-coverage-mcp\",\n      \"env\": {\n        \"COVERAGE_MCP_ALLOWED_ROOT\": \"/path/to/your/repo\"\n      }\n    }\n  }\n}\n```\n\n### Run from source\n\nTo run from source instead of the global tool, use `dotnet run`:\n\n```json\n{\n  \"mcpServers\": {\n    \"coverage\": {\n      \"command\": \"dotnet\",\n      \"args\": [\"run\", \"--project\", \"<path-to-dotnet-coverage-mcp>\"],\n      \"transport\": \"stdio\"\n    }\n  }\n}\n```\n\nOr point directly at the compiled executable:\n\n```json\n{\n  \"mcpServers\": {\n    \"coverage\": {\n      \"command\": \"<path-to-dotnet-coverage-mcp>\\\\bin\\\\Debug\\\\net9.0\\\\DotNetCoverageMcp.exe\",\n      \"transport\": \"stdio\"\n    }\n  }\n}\n```\n\n## Tool Parameters\n\n### `GetSourceFiles`\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `path` | string | Yes | Path to a `.cs` file, folder, or `.csproj` project |\n| `lineBudget` | int | No | Max total lines per batch (default: 300). Small files are grouped together; large files get their own batch. |\n\n### `RunTestsWithCoverage`\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `testProjectPath` | string | Yes | Full path to the `.csproj` test project |\n| `filter` | string | Yes | Test filter string (matched against `FullyQualifiedName`). Use `*` or `,` for broad runs across multiple test classes. |\n| `workingDir` | string | No | Working directory; defaults to the project directory |\n| `forceRestore` | bool | No | When `true`, skips the `--no-restore` flag. Use after scaffolding a new test project or adding NuGet packages. |\n| `sessionId` | string | No | Isolates output directories (`TestResults-{hash}/`, `coveragereport-{hash}/`) and state files for concurrent multi-agent use. |\n| `includeClass` | string | No | Restrict coverage collection to types matching this name (coverlet `Include` filter, applied via a generated runsettings file passed with `--settings`). Independent of `filter` — pass an explicit value to scope coverage; omit it to collect coverage for everything the run touches. Namespace-qualified names are not supported. |\n| `skipReport` | bool | No | When `true`, skips the `reportgenerator` JSON-summary step and returns only the Cobertura XML path. Faster for the inner test loop, where `GetFileCoverage`/`GetUncoveredBranches`/`GetCoverageDiff` read the XML directly. Leave `false` (default) when you need `GetCoverageSummary`'s `Summary.json`. |\n\n### `GetCoverageSummary`\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `summaryJsonPath` | string | Yes | Full path to the generated `Summary.json` file |\n| `belowTarget` | double | No | When set (a fraction in `[0,1]`, e.g. `0.8`), return only classes whose line OR branch coverage is below this threshold. Omit for all classes. |\n| `topN` | int | No | Return only the N lowest-branch-coverage classes (results are sorted worst-first). Omit for all classes. |\n| `methodsPerClass` | int | No | Keep at most this many lowest-branch-coverage methods per class, trimming the rest. Omit to keep all methods. |\n\n### `GetFileCoverage`\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `coberturaXmlPath` | string | Yes | Path to `coverage.cobertura.xml` (falls back to `.mcp-coverage/.coverage-state` if not found) |\n| `sourceFileName` | string | Yes | Source file name to look up (e.g., `ExampleService.cs`) |\n| `sessionId` | string | No | Resolves session-scoped state file for concurrent isolation. |\n| `targetRate` | double | No | Coverage threshold (0.0–1.0) used to compute `allMeetTarget`. Default `0.8`. |\n\n### `GetUncoveredBranches`\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `coberturaXmlPath` | string | Yes | Path to `coverage.cobertura.xml` (falls back to `.mcp-coverage/.coverage-state` if not found) |\n| `methodName` | string | Yes | Method name to inspect (partial match supported; returns all matching methods) |\n| `sessionId` | string | No | Resolves session-scoped state file for concurrent isolation. |\n\n### `GetCoverageDiff`\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `coberturaXmlPath` | string | Yes | Path to the current `coverage.cobertura.xml` |\n| `workingDir` | string | No | Directory for storing baseline; defaults to the XML's parent directory |\n| `sessionId` | string | No | Isolates baseline as `.coverage-prev-{hash}.xml` and resolves session-scoped state file. |\n\n### `AppendTestCode`\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `testFilePath` | string | Yes | Full path to the target `.cs` test file |\n| `codeToAppend` | string | Yes | C# code to insert |\n| `insertAfterAnchor` | string | No | If provided, inserts code after the last occurrence of this string (with whitespace-tolerant fallback). If omitted, appends before the last `}`. |\n\n### `CleanupSession`\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `workingDir` | string | Yes | Project working directory containing `.mcp-coverage/` and TestResults artifacts |\n| `sessionId` | string | No | When set, removes only state files and directories scoped to this session. |\n| `maxAgeMinutes` | int | No | When `sessionId` is omitted, removes artifacts older than this many minutes. Default `120`. |\n\n## State Files\n\nAll 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`.\n\n| File | Purpose |\n|------|---------|\n| `.coverage-state` | Default Cobertura XML path for single-agent use |\n| `.coverage-state-{hash}` | Session-scoped Cobertura XML path |\n| `.coverage-prev.xml` | Default coverage baseline for diff |\n| `.coverage-prev-{hash}.xml` | Session-scoped coverage baseline |\n\n## Plugin (Skills & Agent)\n\nThis repo includes a `plugin/` directory with Claude Code skills and an agent definition for guided test coverage workflows:\n\n```\nplugin/\n├── plugin.json\n├── agents/\n│   └── test-coverage.agent.md\n└── skills/\n    ├── scaffold-test-files/     — Create test directories and files mirroring source structure\n    ├── run-coverage/            — Run tests and view coverage reports\n    ├── analyze-coverage-gaps/   — Find uncovered branches and compare diffs\n    └── improve-test-coverage/   — Iterative loop to reach 80% coverage\n```\n\nThe skills support NUnit, xUnit, and MSTest with framework-agnostic reference docs in `references/unit.md` and `references/integration.md`.\n\n## Dependencies\n\n| Package | Version | Purpose |\n|---------|---------|---------|\n| `Microsoft.Extensions.Hosting` | 10.0.7 | DI and hosting |\n| `ModelContextProtocol` | 1.2.0 | MCP server framework |\n| `Microsoft.CodeAnalysis.CSharp` | 5.3.0 | Roslyn AST for safe code insertion and accurate method counting (~15MB) |\n\n## Security\n\ndotnet-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](SECURITY.md) for the threat model, hardening recommendations, and how to report a vulnerability.\n\n## Contributing\n\nContributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for development\nsetup, pull request guidelines, and code conventions. Notable changes are tracked\nin [CHANGELOG.md](CHANGELOG.md).\n\n## Releasing\n\nMaintainer-only — release process, NuGet publishing, and MCP registry submission are documented in [RELEASING.md](RELEASING.md).\n",
  "bytes": 16009,
  "sha": "e8fbcd4eeddd25ca0701ea3a36b7ea9a44dc70719b3945dd10d3cf263659533e",
  "repo_slug": "hyeonu-cha/dotnet-coverage-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_hyeonu_cha_dotnet_coverage_mcp_43bcb02e/readme"
}