CovLoupe
An MCP server, command line utility, and library for Ruby SimpleCov test coverage analysis.
What is cov-loupe?
cov-loupe makes SimpleCov coverage data queryable and actionable through three interfaces:
- CLI - command-line execution of single reports or queries
- MCP server - stdio (localhost nonnetwork) server assists AI analysis of your coverage
- Ruby library - Programmatic API for custom tooling
Works with any SimpleCov-generated .resultset.json file—no runtime dependency on your test suite. (New coverage.json file support coming soon.)
Key Features
- ✅ Multiple interfaces - CLI, MCP server, and Ruby API
- Annotated source code -
-s full|uncovered|none/--source full|uncovered|nonewith-c N/--context-lines Nfor context lines - ✅ Staleness detection - Identify outdated coverage (missing files, timestamp mismatches, line count changes)
- ✅ Multi-suite support - Automatic merging of multiple test suites (RSpec + Cucumber, etc.)
- ✅ Flexible path resolution - Works with absolute or relative paths
- ✅ Comprehensive error handling - Context-aware messages for each mode
Practical Use Cases
- Query coverage data from AI assistants, e.g.:
- "Using cov-loupe, analyze test coverage data and write a report to a markdown file containing a free text analysis of each issue and then two tables, one sorted in descending order of urgency, the other in ascending order of level of effort."
- "Using cov-loupe, generate a table of directories and their average coverage rates, in ascending order of coverage."
- Find files with the lowest coverage
- Investigate specific files or directories
- Generate CI/CD coverage reports
- Create custom pass/fail predicates for scripts and CI - use the library API or CLI JSON output to implement arbitrarily complex coverage rules beyond simple thresholds (e.g., require higher coverage for critical paths, exempt test utilities, track coverage trends)
Quick Start
Installation
gem install cov-loupe
# Or, for the most current version you may need to specify prerelease:
gem install --pre cov-loupe
Upgrading
If you are upgrading from a previous version, please refer to the Migration Guides.
Generate Coverage Data
# Generate your SimpleCov test coverage data with your test suite run command, e.g.:
bundle exec rspec
# Verify coverage was generated
ls -l coverage/.resultset.json
Basic Usage
CLI - View Coverage Table: This is the default command, so any of the following will work:
cov-loupe
cov-loupe l
cov-loupe list
CLI - Check Specific File:
cov-loupe summary lib/cov_loupe/model/model.rb
# or use abbreviations: s (summary), u (uncovered)
cov-loupe s lib/cov_loupe/model/model.rb
cov-loupe u lib/cov_loupe/cli.rb
CLI - Find Project Resources:
The repo URL, doc server URL, and local gem filespec are output in the header of the online help, e.g.:
Repository: https://github.com/keithrbennett/cov-loupe
Documentation (Web): https://keithrbennett.github.io/cov-loupe/
Documentation (Local): /Users/kbennett/.local/share/mise/installs/ruby/4.0.5/lib/ruby/gems/4.0.0/gems/cov-loupe-VERSION/README.md
There is a p/--path-for option that will get an individual value for each of these:
cov-loupe -p repo
cov-loupe -p docs
cov-loupe -p docs-local
You can use your operating system's application open command (usually open for Mac, xdg-open on Linux, and start on Windows) to assemble this into a single command:
open `cov-loupe -p docs`
Ruby Library:
require "cov_loupe"
model = CovLoupe::CoverageModel.new
list_result = model.list
files = list_result["files"]
# => [{ "file" => "/path/to/project/lib/cov_loupe/model/model.rb", "covered" => 114, "total" => 118, "percentage" => 96.61, "stale" => "ok" }, ...]
summary = model.summary_for("lib/cov_loupe/model/model.rb")
# => { "file" => "/path/to/project/lib/cov_loupe/model/model.rb", "summary" => { "covered" => 114, "total" => 118, "percentage" => 96.61 } }
Use model.relativize(...) when you want library payloads with project-relative paths. See Library API for details.
MCP Server: See MCP Integration Guide for AI assistant setup.
Important for MCP users: MCP servers must keep stdout clean until the protocol handshake begins. If cov-loupe -m mcp prints text such as Resolving dependencies... before responding, see Troubleshooting and MCP Integration for wrapper and RubyGems launcher guidance.
Multi-Suite Coverage
Projects with multiple test suites (RSpec + Cucumber, etc.) are automatically merged. See Multi-Suite Coverage Merging for details and current limitations.
Documentation Index
Full documentation is available at https://keithrbennett.github.io/cov-loupe/.
User Guides:
- Quick Start - Get up and running in 3 steps
- User Docs Overview - Map of all end-user guides
- Installation - Setup for different environments
- CLI Usage - Command-line reference
- Examples - Common use cases
- Advanced Usage - Staleness detection, error modes, path resolution
- Library API - Ruby API documentation
- Error Handling - Error modes and exceptions
- MCP Integration - AI assistant configuration
- Troubleshooting - Common issues
Special Topics & Prompts:
- CLI Fallback for LLMs - When MCP isn't available
- Sample MCP Prompts - Ready-to-use ChatGPT/Claude/Gemini prompts
- Migration Guides
Developer Docs:
- Developer Docs Overview - Entry point for contributors
- Architecture - Design and internals
- Development Guide - Local dev workflow
- Releasing - Release checklist
- Future Enhancements - Planned improvements
- Architecture Decision Records - Design history
Project Docs & Examples:
Requirements
- Ruby >= 3.2 (required by
mcpgem dependency) mcpgem >= 0.15 and < 1.0- SimpleCov-generated
.resultset.jsonfile simplecovgem >= 0.21
Applications pinned to an older mcp version must upgrade it before installing cov-loupe v6.
JRuby Compatibility
The test suite passes on JRuby, and to the best of our knowledge the project is fully JRuby-compatible. If you encounter any JRuby-specific issues, please open a GitHub issue, including as much detail as possible.
Configuring the Resultset
cov-loupe automatically searches for .resultset.json in standard locations (coverage/.resultset.json, .resultset.json, tmp/.resultset.json). For non-standard locations:
# Command-line option (highest priority) - use -r or --resultset
cov-loupe -r /path/to/your/coverage
# Environment variable (project-wide default)
export COV_LOUPE_OPTS="-r /path/to/your/coverage"
# MCP server configuration
# Add to your MCP client config (used as defaults for MCP tools):
# "args": ["-r", "/path/to/your/coverage"]
MCP precedence: For MCP tool calls, per-request JSON parameters win over the CLI args used to start the server (including COV_LOUPE_OPTS). If neither is provided, built-in defaults are used (root: '.', raise_on_stale: false, etc.). Coverage data is cached globally and automatically reloaded when the resultset file changes.
See CLI Usage Guide for complete details.
Common Workflows
Find Coverage Gaps
# Files with worst coverage
cov-loupe -o d list # -o = --sort-order, d = descending (worst at end)
cov-loupe list | less # display table in pager, best files first (worst at end)
cov-loupe list | head -10 # truncate the table
# Filter to specific patterns (see COV_LOUPE_OPTS best practice below)
cov-loupe -g "lib/cov_loupe/tools/**/*.rb" list # -g = --tracked-globs
# Export for analysis
cov-loupe -fJ list > coverage-report.json
Best Practice: Match SimpleCov Configuration
For accurate coverage tracking and validation, set COV_LOUPE_OPTS to match your SimpleCov track_files patterns. See --tracked-globs for the canonical setup example and default behavior.
Working with JSON Output
The -fJ flag enables programmatic processing of coverage data using command-line JSON tools.
Using jq:
# Filter files below 80% coverage
cov-loupe -fJ list | jq '.files[] | select(.percentage < 80)'
Using Ruby one-liners:
# Count files below threshold
cov-loupe -fJ list | ruby -r json -e '
puts JSON.parse($stdin.read)["files"].count { |f| f["percentage"] < 80 }
'
Using rexe:
rexe is a Ruby gem that enables shorter Ruby command lines by providing command-line options for input and output formats, plus other conveniences. It eliminates the need for explicit JSON parsing and formatting code.
Install: gem install rexe
# Filter files below 80% coverage with pretty-printed JSON output
cov-loupe -fJ list | rexe -ij -mb -oJ 'self["files"].select { |f| f["percentage"] < 80 }'
# Count files below threshold
cov-loupe -fJ list | rexe -ij -mb -op 'self["files"].count { |f| f["percentage"] < 80 }'
# Human-readable output with AmazingPrint
cov-loupe -fJ list | rexe -ij -mb -oa 'self["files"].first(3)'
With rexe's -ij -mb options, self automatically becomes the parsed JSON object. The same holds true for JSON output -- using -oJ produces pretty-printed JSON without explicit formatting calls. Rexe also supports YAML input/output (-iy, -oy) and AmazingPrint output (-oa) for human consumption.
When Coverage Rows Are Skipped
If a coverage row has corrupt or malformed data, the CLI now logs and warns after rendering the report. This lets operators immediately see that totals may be incomplete. Example table output:
$ cov-loupe list
┌─────────────────────────────┬──────────┬─────────┬───────┬───────┐
│ File │ % │ Covered │ Total │ Stale │
├─────────────────────────────┼──────────┼─────────┼───────┼───────┤
│ lib/foo.rb │ 66.67% │ 2 │ 3 │ │
│ lib/bar.rb │ 33.33% │ 1 │ 3 │ │
└─────────────────────────────┴──────────┴─────────┴───────┴───────┘
Files: total 2, ok 2, stale 0
WARNING: 1 coverage row skipped due to errors:
- lib/corrupt.rb: Invalid coverage line array: contains non-integer elements: ["bad"]
Run again with --raise-on-stale to exit when rows are skipped.
Multi-line, indented JSON (-fJ) reports still emit valid JSON to stdout; the warning continues to be printed on stderr:
$ cov-loupe -fJ list
{
"files": [
{ "file": "lib/foo.rb", "covered": 2, "total": 3, "percentage": 66.67, "stale": "ok" },
{ "file": "lib/bar.rb", "covered": 1, "total": 3, "percentage": 33.33, "stale": "ok" }
],
"skipped_files": [
{
"file": "lib/corrupt.rb",
"error": "Invalid coverage line array: contains non-integer elements: [\"bad\"]",
"error_class": "CovLoupe::CoverageDataError"
}
],
"missing_tracked_files": [],
"newer_files": [],
"deleted_files": [],
"length_mismatch_files": [],
"unreadable_files": [],
"timestamp_status": "ok",
"counts": { "total": 2, "ok": 2, "stale": 0 }
}
WARNING: 1 coverage row skipped due to errors:
- lib/corrupt.rb: Invalid coverage line array: contains non-integer elements: ["bad"]
Run again with --raise-on-stale to exit when rows are skipped.
Use --raise-on-stale true (or -S true) to turn these warnings into hard failures for CI pipelines.
Run rexe -h to see all available options, or visit the rexe project page for more examples.
For comprehensive JSON processing examples, see user/EXAMPLES.md.
CI/CD Integration
# Fail build if coverage is stale (--raise-on-stale or -S)
cov-loupe --raise-on-stale true list || exit 1
# Generate coverage report artifact
cov-loupe -fJ list > artifacts/coverage.json
Investigate Specific Files
# Quick summary
cov-loupe summary lib/cov_loupe/model/model.rb
# See uncovered lines
cov-loupe uncovered lib/cov_loupe/cli.rb
# View in context
cov-loupe -s u -c 3 uncovered lib/cov_loupe/cli.rb # -s = --source (u = uncovered, n = none to disable), -c = --context-lines
# Detailed hit counts
cov-loupe detailed lib/cov_loupe/coverage/coverage_calculator.rb
# Project totals
cov-loupe totals
cov-loupe -fJ totals
Boolean CLI Options
Boolean flags such as --color (short: -C) and --raise-on-stale (short: -S) require explicit boolean arguments. Recognized literals:
| | |
|--------|--------|
| yes | no |
| y | n |
| true | false|
| t | f |
| on | off |
| + | - |
| 1 | 0 |
Each row lists the equivalent true token (left) and false token (right).
cov-loupe --color false # disable ANSI colors explicitly
cov-loupe -C false # short form
cov-loupe --raise-on-stale yes # enforce stale coverage failures
Commands and Tools
CLI Subcommands: list (l), summary (s), uncovered (u), detailed (d), raw (r), totals (t), validate (v)
MCP Tools: file_coverage_summary, file_coverage_detailed, file_coverage_raw, file_uncovered_lines, project_coverage, project_coverage_totals, project_validate, help, version
📖 See also:
- CLI Usage Guide - Complete command-line reference
- MCP Integration Guide - MCP tools documentation
Troubleshooting
- "command not found" - See Installation Guide
- "cannot load such file -- mcp" - Requires Ruby >= 3.2. Verify:
ruby -v - "Could not find .resultset.json" - Ensure SimpleCov is configured in your test suite, then run tests to generate coverage. See the Configuring the Resultset section for more details.
- MCP server won't connect - Check PATH and Ruby version in MCP Troubleshooting
- RVM in sandboxed environments (macOS) - RVM requires
/bin/pswhich may be blocked by sandbox restrictions. Use rbenv or chruby instead.
For more detailed help, see the full Troubleshooting Guide.
Development
# Clone and setup
git clone https://github.com/keithrbennett/cov-loupe.git
cd cov-loupe
bundle install
# Run tests
bundle exec rspec
# Test locally
bundle exec exe/cov-loupe
# Build and install
gem build cov-loupe.gemspec
gem install cov-loupe-*.gem
See dev/DEVELOPMENT.md for more.
SimpleCov Dependency
cov-loupe declares a runtime dependency on simplecov (>= 0.21) to support multi-suite merging using SimpleCov's combine helpers. The dependency is lazy-loaded only when needed, ensuring fast startup for single-suite projects.
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass (
bundle exec rspec) - Submit a pull request
License
MIT License - see LICENSE file for details.
Links
- GitHub: https://github.com/keithrbennett/cov-loupe
- RubyGems: https://rubygems.org/gems/cov-loupe
- Issues: https://github.com/keithrbennett/cov-loupe/issues
- Changelog: RELEASE_NOTES.md
Next Steps
📦 Install: gem install cov-loupe
📖 Read: CLI Usage Guide | MCP Integration
🐛 Report issues: GitHub Issues
⭐ Star the repo if you find it useful!