使用说明
.net覆盖mcp
一个MCP(模型上下文协议)服务器,为人工智能助手(Claude Code、Gemini CLI等)提供直接访问。NET测试覆盖率工具。运行dottest,解析Cobertura XML,识别未覆盖的分支,运行之间的覆盖率差异,并在整个stdio中附加测试代码。
目的
该服务器允许AI助手运行单元测试、收集覆盖率数据并分析结果,所有这些都不需要离开聊天室。而不是手动运行 dotnet test AI可以直接调用服务器的工具来:
- 发现源文件并按行预算构建智能批处理
- 运行一组经过筛选的测试并收集覆盖率
- 阅读紧凑、人工智能优化的覆盖率摘要(方法级线路/分支率)
- 根据可配置的目标速率(默认80%)检查每个文件的覆盖率
- 将未覆盖的分支识别为结构化JSON
- 跑步之间的覆盖范围不同,只看有什么变化
- 使用原子写入将新测试代码附加到现有测试文件
运作原理
服务器作为控制台进程启动,并通过以下方式进行通信 标准 使用MCP协议。MCP兼容客户端(Claude Code、Gemini CLI等)启动流程并调用其工具,就像它们是函数一样。
AI Client <--stdio/MCP--> dotnet-coverage-mcp <--shell--> dotnet test + reportgenerator
可用工具
| 工具 | 说明 |
|---|---|
GetSourceFiles |
发现 .cs 文件、文件夹或 .csproj 项目。返回文件元数据(行数、方法计数)和按以下方式分组的智能批处理 lineBudget. |
RunTestsWithCoverage |
快跑 dotnet test 使用XPlat代码覆盖率,通过以下方式生成JSON摘要 reportgenerator。返回到的路径 Summary.json 和 coverage.cobertura.xml.支持 forceRestore 和 sessionId 用于并发隔离。 |
GetCoverageSummary |
解析 Summary.json 将结构化类/方法覆盖率数据按分支覆盖率从低到低排序。可选的 belowTarget/topN/methodsPerClass 过滤器会根据仍需处理的内容调整响应 |
GetFileCoverage |
从Cobertura XML获取单个源文件的覆盖率。退货 allMeetTarget (当所有类都符合配置时为true targetRate 用于线路和分支覆盖;默认值0.8)。支持 sessionId. |
GetUncoveredBranches |
查找与给定名称匹配的方法的未覆盖分支条件。返回所有支持部分名称的匹配方法。支持 sessionId. |
GetCoverageDiff |
将当前Cobertura XML与基线进行比较。显示方法级别的更改,包括新方法和删除的方法。支持 sessionId 用于并发隔离。 |
AppendTestCode |
将C#测试代码插入或附加到测试文件中。支持基于锚点的插入,具有空格容忍回退匹配功能。使用原子写入来防止文件损坏。 |
CleanupSession |
删除会话状态文件和 TestResults/coveragereport 目录。通过 sessionId 查看范围,或忽略清理比 maxAgeMinutes (默认值120)。 |
批处理工作流
对于具有许多源文件的项目,建议的工作流程是:
- 发现 --呼叫
GetSourceFiles在文件夹或.csproj获取所有文件和智能批处理 - 跑一次 --呼叫
RunTestsWithCoverage使用宽滤波器(例如。,*)收集所有文件的覆盖率 - 按文件检查 --呼叫
GetFileCoverage对于当前批处理中的每个文件(即时XML解析,无需重新运行测试) - 聚焦 --选择3种最低的分支覆盖率方法并调用
GetUncoveredBranches对于每个 - 编写测试 --使用
AppendTestCode添加测试方法 - 重新运行和差异 --运行一次测试,调用
GetCoverageDiff验证改进 - 重复 --继续,直到批处理文件达到目标速率(默认80%)或3个周期没有改进,然后移动到下一批
这最大限度地减少了 dotnet test 调用(主要瓶颈),同时仍跟踪每个文件的进度。
并发
通过传递一个命令,多个AI代理可以并行运行 sessionId 对于每个工具调用,它隔离了它们的覆盖工件:
- 独立输出目录 —
RunTestsWithCoverage创造TestResults-{hash}/和coveragereport-{hash}/每个会话,防止一个代理在解析过程中删除另一个代理的XML - 作用域状态文件 --覆盖状态写入
.mcp-coverage/.coverage-state-{hash},所以ResolveCoberturaPath解析为每个会话的正确XML - 范围基线 —
GetCoverageDiff将基线存储为.coverage-prev-{hash}.xml每次会话 - 原子写入 --所有文件写入(状态文件和测试代码)都使用write-to-temp,然后重命名,以防止竞争条件或进程崩溃造成的损坏
限制——构建输出不在会话范围内。
sessionId隔离覆盖率 人工制品,而不是。网 建造.dotnet test将目标项目编译为其共享obj/和bin/,不是每个会话,因此两个代理正在运行RunTestsWithCoverage反对 相同 测试项目同时在这些输出上发生冲突并失败buildError(例如。CS2012: the file is being used by another process).运行并行代理 不同的 测试项目,或存储在仓库的单独工作副本上。一个项目上有多个代理是可以的,只要他们dotnet test构建不会重叠。
没有 sessionId,工具使用共享默认值——对单个代理使用是安全的。
需求
-
.NET 9.0 SDK(或更高版本) — https://dotnet.microsoft.com/download
-
报表生成器 全局工具——服务器向其发送shell以呈现覆盖率报告(安装在 安装 下一步)
-
MCP兼容客户端(Claude Code、Gemini CLI等)
-
COVERAGE_MCP_ALLOWED_ROOT--推荐。设置为存储库根目录,以限制每个工具对该子树的文件系统访问。客户端在此根目录之外传递的任何路径都将被拒绝pathNotAllowed未设置时,服务器会记录一次警告并接受任何路径(向后兼容,但不建议用于共享环境)。export COVERAGE_MCP_ALLOWED_ROOT=/path/to/your/repo
安装
将服务器安装为全局服务器。NET工具从NuGet:
dotnet tool install --global dotnet-coverage-mcp
服务器依赖于 报表生成器 呈现覆盖率报告的全局工具--也安装它:
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 代码
claude mcp add coverage --env COVERAGE_MCP_ALLOWED_ROOT=/path/to/your/repo -- dotnet-coverage-mcp
克劳德桌面
增添 claude_desktop_config.json (设置→ 开发者→ 编辑配置):
{
"mcpServers": {
"coverage": {
"command": "dotnet-coverage-mcp",
"env": {
"COVERAGE_MCP_ALLOWED_ROOT": "/path/to/your/repo"
}
}
}
}
光标
增添 ~/.cursor/mcp.json (全球)或 .cursor/mcp.json (每个项目):
{
"mcpServers": {
"coverage": {
"command": "dotnet-coverage-mcp",
"env": {
"COVERAGE_MCP_ALLOWED_ROOT": "/path/to/your/repo"
}
}
}
}
VS代码(GitHub副本)
增添 .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。离开 false (默认)需要时 GetCoverageSummarys Summary.json. |
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 |
用于计算的双 | 否 | 覆盖阈值(0.0-1.0) allMeetTarget.默认值 0.8. |
GetUncoveredBranches
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
coberturaXmlPath |
string | 是 | 路径 coverage.cobertura.xml (回到 .mcp-coverage/.coverage-state 如果未找到) |
methodName |
string | Yes | 要检查的方法名称(支持部分匹配;返回所有匹配的方法) |
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/ 和测试结果工件 |
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) |
安全
netscoverage-mcp作为本地stdio进程运行,并验证每个工具参数 COVERAGE_MCP_ALLOWED_ROOT 限制文件系统访问。看 安全.md 对于威胁模型,强化建议,以及如何报告漏洞。
贡献
欢迎捐款。看 贡献.md 为了发展 设置、拉取请求准则和代码约定。跟踪显著变化 在 更改日志.md.
释放
仅限维护者——发布过程、NuGet发布和MCP注册表提交记录在 发布.md.
