Skip to main content
Glama
Hyeonu-Cha

dotnet-coverage-mcp

by Hyeonu-Cha

dotnet-coverage-mcp

build tests NuGet License: MIT

一个 MCP(模型上下文协议)服务器,让 AI 助手(如 Claude Code、Gemini CLI 等)直接访问 .NET 测试覆盖率工具。通过 stdio 运行 dotnet test、解析 Cobertura XML、识别未覆盖的分支、比较两次运行之间的覆盖率差异,并追加测试代码。

目的

该服务器让 AI 助手无需离开聊天即可运行单元测试、收集覆盖率数据并分析结果。AI 无需手动运行 dotnet test 和解析报告,而是可以直接调用服务器的工具来:

  • 发现源文件并按行预算构建智能批次

  • 运行一组筛选后的测试并收集覆盖率

  • 读取紧凑、面向 AI 优化的覆盖率摘要(方法级行/分支覆盖率)

  • 根据可配置的目标覆盖率(默认 80%)检查每个文件的覆盖率

  • 以结构化 JSON 识别未覆盖的分支

  • 比较两次运行之间的覆盖率差异,仅查看变化内容

  • 以原子写入方式将新测试代码追加到现有测试文件

Related MCP server: codecov-mcp-server

工作原理

服务器以控制台进程启动,并通过 stdio 使用 MCP 协议进行通信。兼容 MCP 的客户端(如 Claude Code、Gemini CLI 等)启动该进程,并像调用函数一样调用其工具。

AI Client  <--stdio/MCP-->  dotnet-coverage-mcp  <--shell-->  dotnet test + reportgenerator

可用工具

工具

描述

GetSourceFiles

从文件、文件夹或 .csproj 项目中查找 .cs 文件。返回文件元数据(行数、方法数)以及按 lineBudget 分组的智能批次。

RunTestsWithCoverage

使用 XPlat Code Coverage 运行 dotnet test,通过 reportgenerator 生成 JSON 摘要。返回 Summary.json 和 coverage.cobertura.xml 的路径。支持 forceRestore 和 sessionId 以实现并发隔离。

GetCoverageSummary

将 Summary.json 解析为结构化的类/方法覆盖率数据,按分支覆盖率从最差到最好排序。可选的 belowTarget/topN/methodsPerClass 过滤器可精简响应,仅保留仍需改进的部分。

GetFileCoverage

从 Cobertura XML 获取单个源文件的覆盖率。返回 allMeetTarget(当所有类在行覆盖率和分支覆盖率上都达到配置的 targetRate 时返回 true;默认 0.8)。支持 sessionId。

GetUncoveredBranches

查找与给定名称匹配的方法中未覆盖的分支条件。返回所有匹配的方法,支持部分名称匹配。支持 sessionId。

GetCoverageDiff

将当前 Cobertura XML 与基线进行比较。显示方法级更改,包括新增和移除的方法。支持 sessionId 以实现并发隔离。

AppendTestCode

将 C# 测试代码插入或追加到测试文件中。支持基于锚点的插入,并带有容忍空白的回退匹配。使用原子写入防止文件损坏。

CleanupSession

删除会话状态文件和 TestResults/coveragereport 目录。传入 sessionId 以限定范围,或省略以清理早于 maxAgeMinutes(默认 120)的工件。

批次工作流

对于包含许多源文件的项目,推荐的工作流程如下:

  1. 发现 — 对文件夹或 .csproj 调用 GetSourceFiles 以获取所有文件和智能批次

  2. 运行一次 — 使用宽泛过滤器(例如 *)调用 RunTestsWithCoverage 以收集所有文件的覆盖率

  3. 逐文件检查 — 对当前批次中的每个文件调用 GetFileCoverage(即时 XML 解析,无需重新运行测试)

  4. 聚焦 — 选择分支覆盖率最低的 3 个方法,并分别调用 GetUncoveredBranches

  5. 编写测试 — 使用 AppendTestCode 添加测试方法

  6. 重新运行并比较 — 运行一次测试,调用 GetCoverageDiff 验证改进

  7. 重复 — 继续直到批次文件达到目标覆盖率(默认 80%)或连续 3 个周期无改进,然后移至下一批次

这最大限度地减少了 dotnet test 的调用次数(主要瓶颈),同时仍能跟踪每个文件的进度。

并发

多个 AI 代理可以通过向每个工具调用传递 sessionId 来并行运行,从而隔离各自的覆盖率工件:

  • 隔离的输出目录 — RunTestsWithCoverage 为每个会话创建 TestResults-{hash}/ 和 coveragereport-{hash}/,防止一个代理在解析过程中删除另一个代理的 XML

  • 作用域状态文件 — 覆盖率状态写入 .mcp-coverage/.coverage-state-{hash},因此 ResolveCoberturaPath 为每个会话解析到正确的 XML

  • 作用域基线 — GetCoverageDiff 为每个会话将基线存储为 .coverage-prev-{hash}.xml

  • 原子写入 — 所有文件写入(状态文件和测试代码)都使用先写临时文件再重命名的方式,以防止因竞争条件或进程崩溃而损坏

限制 — 构建输出不按会话隔离。 sessionId 隔离的是覆盖率工件,而不是 .NET 构建。dotnet test 将目标项目编译到其共享的 obj/ 和 bin/ 目录中,这些目录不是按会话隔离的,因此两个代理同时对同一个测试项目运行 RunTestsWithCoverage 会在这些输出上发生冲突,并以 buildError 失败(例如 CS2012: 该文件正被另一个进程使用)。请针对不同的测试项目运行并行代理,或使用仓库的独立工作副本。只要它们的 dotnet test 构建不重叠,多个代理在一个项目上运行是可以的。

如果不使用 sessionId,工具将使用共享默认值——对于单代理使用是安全的。

要求

  • .NET 9.0 SDK(或更高版本) — https://dotnet.microsoft.com/download

  • reportgenerator 全局工具 — 服务器调用它来渲染覆盖率报告(在下面的 安装 步骤中安装)

  • 兼容 MCP 的客户端(Claude Code、Gemini CLI 等)

  • COVERAGE_MCP_ALLOWED_ROOT — 推荐设置。将其设置为你的仓库根目录,以限制每个工具的文件系统访问仅限于该子树。客户端传入的任何超出此根目录的路径都会被拒绝并返回 pathNotAllowed。如果未设置,服务器会记录一次警告并接受任何路径(向后兼容,但不推荐用于共享环境)。

    export COVERAGE_MCP_ALLOWED_ROOT=/path/to/your/repo

安装

从 NuGet 将服务器安装为全局 .NET 工具:

dotnet tool install --global dotnet-coverage-mcp

服务器依赖 reportgenerator 全局工具来渲染覆盖率报告——也请安装它:

dotnet tool install --global dotnet-reportgenerator-globaltool

安装后,dotnet-coverage-mcp 命令将位于你的 PATH 中。

构建与运行(从源代码)

cd <path-to-dotnet-coverage-mcp>

# Restore dependencies
dotnet restore

# Build
dotnet build

# Run
dotnet run

服务器将启动并等待通过 stdin/stdout 接收 MCP 消息。

MCP 客户端配置

安装全局工具(dotnet tool install --global dotnet-coverage-mcp)后, 将服务器注册到你的 MCP 客户端。将 COVERAGE_MCP_ALLOWED_ROOT 设置为 你希望服务器操作的仓库。

Claude Code

claude mcp add coverage --env COVERAGE_MCP_ALLOWED_ROOT=/path/to/your/repo -- dotnet-coverage-mcp

Claude Desktop

添加到 claude_desktop_config.json(设置 → 开发者 → 编辑配置):

{
  "mcpServers": {
    "coverage": {
      "command": "dotnet-coverage-mcp",
      "env": {
        "COVERAGE_MCP_ALLOWED_ROOT": "/path/to/your/repo"
      }
    }
  }
}

Cursor

添加到 ~/.cursor/mcp.json(全局)或 .cursor/mcp.json(项目级):

{
  "mcpServers": {
    "coverage": {
      "command": "dotnet-coverage-mcp",
      "env": {
        "COVERAGE_MCP_ALLOWED_ROOT": "/path/to/your/repo"
      }
    }
  }
}

VS Code (GitHub Copilot)

添加到 .vscode/mcp.json:

{
  "servers": {
    "coverage": {
      "type": "stdio",
      "command": "dotnet-coverage-mcp",
      "env": {
        "COVERAGE_MCP_ALLOWED_ROOT": "/path/to/your/repo"
      }
    }
  }
}

从源代码运行

要从源代码运行而不是使用全局工具,请使用 dotnet run:

{
  "mcpServers": {
    "coverage": {
      "command": "dotnet",
      "args": ["run", "--project", "<path-to-dotnet-coverage-mcp>"],
      "transport": "stdio"
    }
  }
}

或直接指向编译后的可执行文件:

{
  "mcpServers": {
    "coverage": {
      "command": "<path-to-dotnet-coverage-mcp>\\bin\\Debug\\net9.0\\DotNetCoverageMcp.exe",
      "transport": "stdio"
    }
  }
}

工具参数

GetSourceFiles

参数

类型

必填

描述

path

string

是

指向 .cs 文件、文件夹或 .csproj 项目的路径

lineBudget

int

否

每个批次的最大总行数(默认:300)。小文件会分组在一起;大文件会获得自己的批次。

RunTestsWithCoverage

参数

类型

必填

描述

testProjectPath

string

是

.csproj 测试项目的完整路径

filter

string

是

测试筛选字符串(匹配 FullyQualifiedName)。使用 * 或 , 在多个测试类之间进行广泛运行。

workingDir

string

否

工作目录;默认为项目目录

forceRestore

bool

否

当为 true 时,跳过 --no-restore 标志。在创建新测试项目或添加 NuGet 包后使用。

sessionId

string

否

隔离输出目录(TestResults-{hash}/、coveragereport-{hash}/)和状态文件,用于并发多代理使用。

includeClass

string

否

将覆盖率收集限制为与此名称匹配的类型(coverlet Include 筛选器,通过生成的 runsettings 文件以 --settings 方式传入)。与 filter 独立——显式传入值以限定覆盖范围;省略则以收集本次运行触及到的所有内容的覆盖率。不支持命名空间限定的名称。

skipReport

bool

否

当为 true 时,跳过 reportgenerator JSON 摘要步骤,仅返回 Cobertura XML 路径。在内部测试循环中更快,此时 GetFileCoverage/GetUncoveredBranches/GetCoverageDiff 直接读取 XML。当需要 GetCoverageSummary 的 Summary.json 时,请保持 false(默认值)。

GetCoverageSummary

参数

类型

必填

描述

summaryJsonPath

string

是

生成的 Summary.json 文件的完整路径

belowTarget

double

否

设置时([0,1] 中的小数,例如 0.8),仅返回行覆盖率或分支覆盖率低于此阈值的类。省略则返回所有类。

topN

int

否

仅返回分支覆盖率最低的 N 个类(结果按最差优先排序)。省略则返回所有类。

methodsPerClass

int

否

每个类最多保留这么多分支覆盖率最低的方法,并将其余部分裁剪。省略则保留所有方法。

GetFileCoverage

参数

类型

必填

描述

coberturaXmlPath

string

是

coverage.cobertura.xml 的路径(如未找到则回退到 .mcp-coverage/.coverage-state)

sourceFileName

string

是

要查找的源文件名(例如 ExampleService.cs)

sessionId

string

否

解析会话作用域的状态文件,用于并发隔离。

targetRate

double

否

用于计算 allMeetTarget 的覆盖率阈值(0.0–1.0)。默认 0.8。

GetUncoveredBranches

参数

类型

必填

描述

coberturaXmlPath

string

是

coverage.cobertura.xml 的路径(如未找到则回退到 .mcp-coverage/.coverage-state)

methodName

string

是

要检查的方法名(支持部分匹配;返回所有匹配的方法)

sessionId

string

否

解析会话作用域的状态文件,用于并发隔离。

GetCoverageDiff

参数

类型

必填

描述

coberturaXmlPath

string

是

当前 coverage.cobertura.xml 的路径

workingDir

string

否

存放基线的目录;默认为 XML 的父目录

sessionId

string

否

将基线隔离为 .coverage-prev-{hash}.xml 并解析会话作用域的状态文件。

AppendTestCode

参数

类型

必填

描述

testFilePath

string

是

目标 .cs 测试文件的完整路径

codeToAppend

string

是

要插入的 C# 代码

insertAfterAnchor

string

否

若提供,则在该字符串最后一次出现之后插入代码(带空白容忍回退)。若省略,则追加到最后一个 } 前面。

CleanupSession

参数

类型

必填

描述

workingDir

string

是

包含 .mcp-coverage/ 和 TestResults 工件的项目工作目录

sessionId

string

否

设置时,仅删除此会话作用域内的状态文件和目录。

maxAgeMinutes

int

否

当省略 sessionId 时,将移除早于此分钟数的构件。默认 120。

状态文件

所有状态文件都写入工作目录下的 .mcp-coverage/ 子目录中,保持项目根目录干净。请将 .mcp-coverage/ 添加到目标仓库的 .gitignore 中。

文件

用途

.coverage-state

针对单代理使用的默认 Cobertura XML 文件路径

.coverage-state-{hash}

针对不同会话的不同 Cobertura XML 文件路径

.coverage-prev.xml

默认的 diff 覆盖率基线

.coverage-prev-{hash}.xml

会话作用域的覆盖率基线

插件(技能与代理)

此仓库包含一个 plugin/ 目录,其中包含用于引导测试覆盖率工作流的 Claude Code 技能和代理定义:

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% coverage

这些技能支持 NUnit、xUnit 和 MSTest,并在 references/unit.md 和 references/integration.md 中提供了与框架无关的参考文档。

依赖项

包

版本

用途

Microsoft.Extensions.Hosting

10.0.7

DI 与托管

ModelContextProtocol

1.2.0

MCP 服务器框架

Microsoft.CodeAnalysis.CSharp

5.3.0

Roslyn AST,用于安全代码插入和准确的方法计数(约 15MB)

安全

dotnet-coverage-mcp 作为本地 stdio 进程运行,并会对 COVERAGE_MCP_ALLOWED_ROOT 校验所有工具参数,以限制对文件系统的访问。有关威胁模型、加固建议以及漏洞报告方式,请参阅 SECURITY.md。

参与贡献

我们欢迎各种贡献。有关开发环境搭建、拉取请求指南以及代码规范,请参阅 CONTRIBUTING.md。重要的变更记录在 CHANGELOG.md 中。

发布

仅限维护者——发布流程、NuGet 发布以及 MCP 注册表提交都记录在 RELEASING.md 中。

Related MCP Connectors

Related MCP Servers