Coverctl

io.github.felixgeelhaarPublished by GitHub account felixgeelhaarv1.16.0Updated Oct 24, 2026

Go code coverage analysis tool with MCP server for AI-powered coverage workflows

VerifiedSTDIODesktop onlyDeveloper ToolsSecurity & Monitoring

Overview

AI-generated overview

Runs test coverage checks and enforces per-domain coverage policy so an AI coding agent can catch regressions before committing.

What you can do
Coverctl wraps each language's native coverage runner and exposes it over MCP. In agent mode it advertises three tools: check runs tests with coverage and enforces per-domain minimums, suggest recommends thresholds, and debt ranks where coverage gaps are largest. A CI mode adds init, report, record, compare, badge and pr-comment, plus read-only resources for debt, trend, suggestions and detected config.
When to use it
Use it when an AI agent edits code in a repository with coverage thresholds and you want policy feedback during the edit turn rather than after CI fails. It suits polyglot repositories that want one configuration file and one MCP surface across languages.
Requirements
A local process started over stdio, installed as a platform-specific package (macOS, Linux or Windows). It needs the project's own test runner and toolchain for the detected language, a .coverctl.yaml policy file, and no account, API key or network access.
Before you install
It executes the project's test suite, so tests and their dependencies run on the machine. Agent mode is restricted to typed capabilities and rejects arbitrary runner arguments, but CLI invocations from a terminal are not sanitized. The default install sends no telemetry; an opt-in --mcp-telemetry flag writes tool-call events to stderr. Agents may try to lower thresholds in .coverctl.yaml instead of fixing coverage.

Installation

In SourceWeft

  1. Open Coverctl in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

coverctl

Agent-loop coverage governance for all sorts of languages — coverage your AI coding agent calls before commit, not a dashboard you read after CI.

Works with Python, TypeScript/JavaScript, Java, Rust, Go, C#, C/C++, PHP, Ruby, Swift, Dart, Scala, Elixir, and Shell — one .coverctl.yaml, one MCP surface. Not a Go cover tool.

Get started ↓ · Languages ↓ · What it looks like ↓ · MCP tools ↓ · CLI reference ↓ · Why this exists ↓ · Community ↓

[MCP] [15 languages] [Releases]

"Our AI agents ship code fast, but they're blind to coverage policy while editing. We only see breakage in CI, after context is gone, and the same agent then guesses its way to a fix."

Same policy model on every language: auto-detect the runner, group paths into domains, enforce mins before commit. Mock-heavy or exotic layouts may need an explicit domains: block in .coverctl.yaml.

coverctl is not a Go cover CLI: it is policy + MCP over each language's native coverage runner (vs go test -cover / pytest --cov).

Get started

bash
brew install klarlabs-studio/tap/coverctl

Wire into Claude Code (~/.config/claude-code/mcp.json):

json
{  "mcpServers": {    "coverctl": {      "command": "coverctl",      "args": ["mcp", "serve"]    }  }}

Ask the agent: "Run coverctl check and tell me which domains regressed."

For Cursor / Cline / Claude Desktop / Aider / Continue / OpenCode and other MCP clients, see docs/src/content/docs/mcp.mdx. All MCP-capable clients work; coverctl mcp serve runs in agent mode by default (3 tools: check, suggest, debt). Use --mode=ci for the full nine-tool surface.

Validate the install end-to-end:

bash
coverctl mcp doctor   # PASS/FAIL per step with remediation

What it looks like in the agent loop

Rendered for clarity — your client may format tool-calls differently.

text
You:    Add the email-validation feature to the api domain.
Claude: I'll add EmailValidator + tests under src/api/.
  [edits: src/api/email.ts, src/api/email.test.ts]
  Tool call → coverctl check  Result   → passed: false; failing: api (78.2%, required 80%)             uncovered: src/api/email.ts:42-58 (error paths)
  Tool call → coverctl suggest api  Result   → uncovered files: email.ts (16 lines), retry.ts (4 lines)
  Two error branches in EmailValidator.validate are uncovered.  I'll add cases for empty-input and malformed-domain so api meets  the 80% threshold before we commit.

The regression that used to surface in CI 8 minutes later is fixed in the same agent turn.

The agent does well: reads check output verbatim, calls suggest to find uncovered files, calls debt to rank smallest fixes, re-runs check to confirm.

Watch for: agents lowering thresholds in .coverctl.yaml to "fix" a failure (it's not a fix). Agents claiming coverage rose without a new check call (hallucination). Agents ignoring rejection error_code and retrying with the same input.

coverctl returns deterministic structured signals; the agent's fix still needs a human reading. The full contract is in docs/src/content/docs/mcp.mdx.

Why this exists

AI coding agents write code blind to coverage. They edit, you commit, the regression surfaces in CI minutes or hours later — too late to course-correct in the same session. Dashboards (Codecov, Coveralls) and raw language runners (go test -cover, pytest --cov, nyc) target humans reading numbers after the fact — not agents enforcing policy mid-edit.

coverctl is built for the agent loop (and is not a replacement for Go's cover tooling):

  • Catches regressions before commit. Coverage feedback in the agent's edit turn — not minutes after CI fails. The wedge metric is regressions caught pre-commit.
  • Agent-callable via MCP. Speaks MCP — the multi-vendor agent-tool standard now governed by Anthropic, OpenAI, Google, Microsoft, AWS. Works with every MCP-capable client today; works with whatever ships next without modification.
  • Polyglot governance, one config. One .coverctl.yaml enforces per-domain thresholds across 15 languages. On Go repos it may invoke go test the way it invokes pytest elsewhere — then applies the same domain policy. Agents touch any language; coverage governance must too.
  • Local-first. Your source never leaves the machine. Agent calls coverctl over stdio, not over a SaaS API. No account, no upload, no third-party dependency in the agent's reach.
  • Hardened MCP surface. Input + output sanitization defends against prompt-injection through test-runner flags and hostile filenames in coverage profiles (Lethal Trifecta).

The CLI and MCP server are Apache-2.0 licensed and stay free. A hosted layer for cross-repo coverage history is on the roadmap (see docs/strategy/monetization-decision.md) — additive, not a paywall.

MCP tools

Agent mode advertises three tools (check, suggest, debt) for reliable agent tool selection. CI mode (--mode=ci) adds the rest.

ToolModePurpose
checkagent + ciRun tests with coverage and enforce policy. Returns per-domain pass/fail, files, warnings.
suggestagent + ciRecommend thresholds (current / aggressive / conservative).
debtagent + ciCoverage gap per domain — where to spend effort, ranked.
initciAuto-detect project structure and create .coverctl.yaml with domain policies.
reportciAnalyze an existing coverage profile without running tests.
recordciRecord current coverage to history for trend tracking.
compareciDiff two coverage profiles. Returns delta, improved/regressed files, domain changes.
badgeciGenerate SVG coverage badge.
pr-commentciPost coverage report to GitHub / GitLab / Bitbucket PR.

MCP resources (read-only context)

URIContent
coverctl://debtCoverage debt as JSON.
coverctl://trendTrend over recorded history.
coverctl://suggestThreshold suggestions.
coverctl://configDetected project config.

Security boundaries

coverctl treats MCP traffic as untrusted in both directions, per the Lethal Trifecta threat model.

  • Input boundary. Agent mode accepts typed capabilities (packages, tags, race, short, run, timeout) and rejects arbitrary runner testArgs so coverctl cannot be used as a generic command-execution MCP server. Dangerous flags (--rootdir, --cov-config, -D, -I, --require, --init-script, --node-options, ...) are also denied as defense in depth. Agent mode cannot select an alternate policy file, filter check to a subset of domains, narrow coverageScope (shrinking measurement can hide untested domains), skip the test run via fromProfile, or use incremental check (an empty diff auto-passes); repository .coverctl.yaml is authoritative and verification must execute tests against the full policy. Rejection responses use a stable schema with error_code and agent-actionable remediation. CLI invocations from a human terminal are not sanitized; the human is the trust boundary there.
  • Output boundary. User-controlled identifiers flowing back to the agent (filenames in coverage profiles, domain names, package paths) are percent-encoded rather than destructively replaced, so distinct names stay distinct (src/über.go does not collapse into another file). Control characters, backticks, and markdown metacharacters cannot render as instructions. Prevents return-trip prompt injection through a hostile PR or attacker-named test file.

coverctl is local-first. The default install transmits nothing — no telemetry, no analytics, no source data. An opt-in --mcp-telemetry flag emits structured tool-call events to stderr for users who want to instrument their own pipelines (format documented in docs/design/mcp-metrics-spec.md). Adversarial + happy-path evals (85+ scenarios under internal/eval/scenarios/) gate every release on rejection-schema integrity and prompt-injection resistance.

Canonical intent: docs/strategy/system-intent.md. Full threat model + residual risk: docs/security/mcp-threat-model.md.

CLI reference

The CLI is the substrate behind the MCP server; humans can use it directly.

CommandPurpose
init / iInteractive wizard, auto-detects language and domains. --no-interactive for CI.
check / cRun coverage and enforce policy. -o json for machine output, --fail-under N, --ratchet, --from-profile.
run / rProduce coverage artifacts without policy evaluation.
watch / wRe-run coverage on file change during development.
reportEvaluate an existing profile. -o html, --uncovered, --diff <ref>, --merge <profile>.
detectAuto-detect domains and write config. --dry-run to preview.
badgeSVG coverage badge. --style flat-square.
compareDiff two profiles.
debtCoverage debt report.
trendCoverage trend from recorded history.
recordAppend current coverage to history. --commit, --branch for CI.
suggestThreshold suggestions. --write-config to apply.
pr-commentPost coverage to GitHub/GitLab/Bitbucket PR.
ignoreShow configured excludes and tracked domains.
mcp serveStart MCP server (stdio). --mode=agent|ci|auto.
mcp doctorFirst-run validation: PASS/FAIL per step with remediation.
surveySean Ellis 40% PMF prompt; appends to ~/.coverctl/survey.jsonl.

Global flags: -q/--quiet, --no-color, --ci (combines quiet + GitHub Actions annotations).

Test-execution flags

check, run, record accept toolchain flags forwarded to the underlying test runner:

FlagExample
--tags--tags integration,e2e
--race(Go race detector)
--shortSkip long-running tests
-vVerbose test output
--run--run TestFoo
--timeout--timeout 30m
--test-argRepeatable: --test-arg=-count=1 --test-arg=-parallel=4
--language / -lOverride autodetection: python, javascript, typescript, java, rust, go, csharp, cpp, php, ruby, swift, dart, scala, elixir, shell

Terminal flow (without an agent)

If you prefer running coverctl directly:

bash
coverctl init      # auto-detect language + domains, write .coverctl.yamlcoverctl check     # enforce policy; exit 1 on violationcoverctl suggest --strategy currentcoverctl record

If a step fails: coverctl detect --dry-run previews init output; coverctl check -o json surfaces structured failure detail; coverctl record --commit "$(git rev-parse HEAD)" --branch "$(git rev-parse --abbrev-ref HEAD)" provides metadata explicitly in CI.

Configuration

.coverctl.yaml (schema: schemas/coverctl.schema.json):

Domains are named path groups, not language packages. match uses path globs for most languages (src/auth/**) or Go package paths when the project is Go (./internal/auth/...):

yaml
version: 1policy:  default:    min: 75  domains:    - name: auth      match: ["src/auth/**"]   # Go equivalent: ["./internal/auth/..."]      min: 90                   # critical path — stricter    - name: api      match: ["src/api/**"]      min: 80    - name: utils      match: ["src/utils/**"]      # falls back to default min: 75exclude:  - "**/generated/**"

Per-domain enforcement is the point: overall coverage hides regressions in critical paths. coverctl evaluates each domain against its own minimum and fails the build if any domain falls below. Per-language starters: docs quick start. How this differs from raw runners: vs native coverage.

Go-only footnote: why numbers can differ from plain go test ./...

On Go projects coverctl may pass -coverpkg spanning configured domains so a package is credited for tests of other packages that exercise it. Plain go test ./... credits only a package's own tests — two legitimate numbers; coverctl's is usually higher. This is Go runner behavior, not the product model. Prefer coverctl check profiles when comparing history. --from-profile reports whatever the supplied profile measured.

Advanced

yaml
files:  - match: ["internal/core/*.go"]    min: 90                          # per-file overridesdiff:  enabled: true  base: origin/main                  # only enforce on changed filesintegration:  enabled: true                      # Go 1.20+ GOCOVERDIR integration tests  packages: ["./internal/integration/..."]  cover_dir: ".cover/integration"  profile: ".cover/integration.out"merge:  profiles: [".cover/unit.out", ".cover/integration.out"]annotations:  enabled: true                      # // coverctl:ignore, // coverctl:domain=NAME

Multi-package monorepo? Use extends: for inherited policies. Starting point: copy templates/coverctl.yaml.

Supported languages

One product across 15 languages. coverctl auto-detects which runner to call; you write path domains once.

LanguageFormatDetection markers
PythonCobertura, LCOVpyproject.toml, setup.py, requirements.txt
TypeScript / JavaScriptLCOVtsconfig.json, package.json
Java / KotlinJaCoCo, Coberturapom.xml, build.gradle
RustLCOV (cargo-llvm-cov)Cargo.toml
GoNative cover profilego.mod, go.sum
C# / .NETCobertura (coverlet)*.csproj, *.sln
C / C++LCOV (gcov/lcov)CMakeLists.txt, meson.build
PHPCobertura (PHPUnit)composer.json, phpunit.xml
RubyLCOV (SimpleCov)Gemfile, Rakefile
SwiftLCOV (llvm-cov)Package.swift
DartLCOV (dart test)pubspec.yaml
ScalaCobertura (scoverage)build.sbt
ElixirLCOV (mix test)mix.exs
ShellCobertura (kcov)*.bats

Per-language config examples: docs quick start.

GitHub Action

yaml
jobs:  coverage:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v6      - uses: actions/setup-go@v6        with:          go-version: "1.25"      - uses: ./.github/actions/coverctl        with:          command: check          config: .coverctl.yaml          output: text

For platform & devex teams

Standardizing coverage policy across many polyglot repos? coverctl ships with the artifacts your security and procurement reviews will ask for:

Considering coverctl org-wide? Open a GitHub issue with label platform-evaluation — happy to walk through architecture and trust boundaries.

Community

Used coverctl for a few weeks? Run coverctl survey to share PMF feedback (local-only by default; nothing transmitted unless you opt in).

Built by Felix Geelhaar with contributions from the polyglot AI-coding community.

Contributing

  • TDD: tests before behavior changes.
  • Meet each domain's min in .coverctl.yaml (default 80% when unset). This repo is Go, so dogfood with go run ./cmd/coverctl check (policy over the runner — not a substitute for reading go test failures).
  • Conventional Commits (feat:, fix:, chore:, ...) for Relicta version-bump logic.
  • main is protected; merge via PR after CI green (.github/workflows/go.yml + .github/workflows/eval.yml).
  • Run gofmt -w and golangci-lint v2 before pushing.

Architecture details for contributors: ARCHITECTURE.md.

Releases

Managed by Relicta. Do not push v* tags manually.

Security

See SECURITY.md for disclosure policy. MCP-input sanitization (internal/mcp/sanitize.go) and output canonicalization (internal/mcp/sanitize_output.go) are the primary defenses against prompt-injection-driven argument and content attacks; report bypasses privately.

Source: README.md at commit 2abd070

Tools

0
SourceWeft has not started this package in its sandbox yet, so its tools are not known.

Version history

1
  1. v1.16.0LatestSep 16, 2026

Score

50% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts

    Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.

  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listed15 pts

    Answer tools/list without a credential, or ship a package that starts in a sandbox without one.

  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts