.NET测试覆盖率工具

一个MCP服务器,为AI助手提供直接访问.NET测试覆盖率工具的能力,包括运行测试、解析覆盖率报告、识别未覆盖分支等功能。

  • by Hyeonu-Cha
  • C#
  • 本地部署
  • 2 星标
  • 更新于
在 GitHub 查看 Hyeonu-Cha/TestCoverageMcpServer

使用说明

.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)。

批处理工作流

对于具有许多源文件的项目,建议的工作流程是:

  1. 发现 --呼叫 GetSourceFiles 在文件夹或 .csproj 获取所有文件和智能批处理
  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 每次会话
  • 原子写入 --所有文件写入(状态文件和测试代码)都使用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.