simplecov-mcp 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +379 -32
- data/exe/simplecov-mcp +19 -2
- data/lib/simple_cov/mcp.rb +9 -0
- data/lib/simple_cov_mcp/base_tool.rb +54 -0
- data/lib/simple_cov_mcp/cli.rb +390 -0
- data/lib/simple_cov_mcp/error_handler.rb +131 -0
- data/lib/simple_cov_mcp/error_handler_factory.rb +38 -0
- data/lib/simple_cov_mcp/errors.rb +176 -0
- data/lib/simple_cov_mcp/mcp_server.rb +30 -0
- data/lib/simple_cov_mcp/model.rb +104 -0
- data/lib/simple_cov_mcp/staleness_checker.rb +125 -0
- data/lib/simple_cov_mcp/tools/all_files_coverage_tool.rb +63 -0
- data/lib/simple_cov_mcp/tools/coverage_detailed_tool.rb +29 -0
- data/lib/simple_cov_mcp/tools/coverage_raw_tool.rb +29 -0
- data/lib/simple_cov_mcp/tools/coverage_summary_tool.rb +29 -0
- data/lib/simple_cov_mcp/tools/coverage_table_tool.rb +61 -0
- data/lib/simple_cov_mcp/tools/help_tool.rb +133 -0
- data/lib/simple_cov_mcp/tools/uncovered_lines_tool.rb +29 -0
- data/lib/simple_cov_mcp/tools/version_tool.rb +31 -0
- data/lib/simple_cov_mcp/util.rb +122 -0
- data/lib/simple_cov_mcp/version.rb +5 -0
- data/lib/simple_cov_mcp.rb +102 -0
- data/lib/simplecov_mcp.rb +2 -3
- data/spec/all_files_coverage_tool_spec.rb +46 -0
- data/spec/base_tool_spec.rb +58 -0
- data/spec/cli_error_spec.rb +103 -0
- data/spec/cli_json_source_spec.rb +92 -0
- data/spec/cli_source_spec.rb +37 -0
- data/spec/cli_spec.rb +72 -0
- data/spec/cli_table_spec.rb +28 -0
- data/spec/cli_usage_spec.rb +58 -0
- data/spec/coverage_table_tool_spec.rb +64 -0
- data/spec/error_handler_spec.rb +72 -0
- data/spec/errors_stale_spec.rb +49 -0
- data/spec/fixtures/project1/lib/bar.rb +4 -0
- data/spec/fixtures/project1/lib/foo.rb +5 -0
- data/spec/help_tool_spec.rb +47 -0
- data/spec/legacy_shim_spec.rb +13 -0
- data/spec/mcp_server_spec.rb +78 -0
- data/spec/model_staleness_spec.rb +49 -0
- data/spec/simplecov_mcp_model_spec.rb +51 -32
- data/spec/spec_helper.rb +37 -7
- data/spec/staleness_more_spec.rb +39 -0
- data/spec/util_spec.rb +78 -0
- data/spec/version_spec.rb +10 -0
- metadata +56 -13
- data/lib/simplecov/mcp/base_tool.rb +0 -18
- data/lib/simplecov/mcp/cli.rb +0 -98
- data/lib/simplecov/mcp/model.rb +0 -59
- data/lib/simplecov/mcp/tools/all_files_coverage.rb +0 -28
- data/lib/simplecov/mcp/tools/coverage_detailed.rb +0 -22
- data/lib/simplecov/mcp/tools/coverage_raw.rb +0 -22
- data/lib/simplecov/mcp/tools/coverage_summary.rb +0 -22
- data/lib/simplecov/mcp/tools/uncovered_lines.rb +0 -22
- data/lib/simplecov/mcp/util.rb +0 -94
- data/lib/simplecov/mcp/version.rb +0 -8
- data/lib/simplecov/mcp.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06e96d273c6549d1f0539baa88ec863009f263e6d9f214c66157ac5e708b51a5
|
4
|
+
data.tar.gz: fc1503a39a31d19076f4e7827aad1d1996fd4760b381aafdf7e4b13f8891fb0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 41125a954fca4422327a821f18ad55b8de0d473ea3c3d67bc0bb9eb6105d81aa09efcab262a09f0cedeb809adb73a0e974b36d0dcf00ceff55af8e639a573afe
|
7
|
+
data.tar.gz: 7ab8e6375e1645557a8dd30c72be8e37e9d4fd97de59e330db24c0f6ec92339db5488b43c5cc9dbc975edbf5184fdb3179d5425cb48d5791d2fb0650659ab439
|
data/README.md
CHANGED
@@ -5,56 +5,331 @@ MCP server + CLI for inspecting SimpleCov coverage data.
|
|
5
5
|
This gem provides:
|
6
6
|
|
7
7
|
- An MCP (Model Context Protocol) server exposing tools to query coverage for files.
|
8
|
-
- A
|
8
|
+
- A flexible CLI with subcommands to list all files, show a file summary, print raw coverage arrays, list uncovered lines, and display detailed per-line hits. Supports JSON output, displaying annotated source code (full file or uncovered lines with context), and custom resultset locations.
|
9
|
+
|
10
|
+
## Features
|
11
|
+
|
12
|
+
- MCP server tools for coverage queries: raw, summary, uncovered, detailed, all-files list, and version.
|
13
|
+
- Per-file staleness flag in list outputs to highlight files newer than coverage or with line-count mismatches (shown as a compact '!' column in the CLI).
|
14
|
+
- CLI subcommands: `list`, `summary`, `raw`, `uncovered`, `detailed`, `version` (default `list`).
|
15
|
+
- JSON output with `--json` for machine use; human-readable tables/rows by default.
|
16
|
+
- Annotated source snippets with `--source[=full|uncovered]` and `--source-context N`; optional colors with `--color/--no-color`.
|
17
|
+
- Flexible resultset location: `--resultset PATH` or `SIMPLECOV_RESULTSET`; accepts file or directory; sensible default search order.
|
18
|
+
- Works installed as a gem, via Bundler (`bundle exec`), or directly from this repo’s `exe/` (symlink-safe).
|
19
|
+
|
20
|
+
## SimpleCov Independence
|
21
|
+
|
22
|
+
This codebase does not require, and is not connected to, the `simplecov` library at runtime. Its only interaction is reading the JSON resultset file that SimpleCov generates (`.resultset.json`). As long as that file exists in your project (in a default or specified location), the CLI and MCP server can operate without `require "simplecov"` in your app or test process.
|
9
23
|
|
10
24
|
## Installation
|
11
25
|
|
12
26
|
Add to your Gemfile or install directly:
|
13
27
|
|
14
|
-
```
|
28
|
+
```sh
|
15
29
|
gem install simplecov-mcp
|
16
30
|
```
|
17
31
|
|
18
|
-
Require path is `
|
32
|
+
Require path is `simple_cov_mcp` (also `simplecov_mcp`). Legacy `simple_cov/mcp` is supported via shim. Executable is `simplecov-mcp`.
|
19
33
|
|
20
34
|
## Usage
|
21
35
|
|
22
|
-
|
36
|
+
### Library Usage (Ruby)
|
37
|
+
|
38
|
+
Use this gem programmatically to inspect coverage without running the CLI or MCP server. The primary entry point is `SimpleCovMcp::CoverageModel`.
|
39
|
+
|
40
|
+
Basics:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
require "simple_cov_mcp"
|
44
|
+
|
45
|
+
# Defaults (omit args; shown here with comments):
|
46
|
+
# - root: "."
|
47
|
+
# - resultset: resolved from SIMPLECOV_RESULTSET or common paths under root
|
48
|
+
# - staleness: "off" (no stale checks)
|
49
|
+
# - tracked_globs: nil (no project-level file-set checks)
|
50
|
+
model = SimpleCovMcp::CoverageModel.new
|
51
|
+
|
52
|
+
# Custom configuration (non-default values):
|
53
|
+
model = SimpleCovMcp::CoverageModel.new(
|
54
|
+
root: "/path/to/project", # non-default project root
|
55
|
+
resultset: "build/coverage", # file or directory containing .resultset.json
|
56
|
+
staleness: "error", # enable stale checks (raise on stale)
|
57
|
+
tracked_globs: ["lib/**/*.rb"] # for 'all_files' staleness: flag new/missing files
|
58
|
+
)
|
59
|
+
|
60
|
+
# List all files with coverage summary, sorted ascending by % (default)
|
61
|
+
files = model.all_files
|
62
|
+
# => [ { 'file' => '/abs/path/lib/foo.rb', 'covered' => 12, 'total' => 14, 'percentage' => 85.71, 'stale' => false }, ... ]
|
63
|
+
|
64
|
+
# Per-file summaries
|
65
|
+
summary = model.summary_for("lib/foo.rb")
|
66
|
+
# => { 'file' => '/abs/.../lib/foo.rb', 'summary' => {'covered'=>12, 'total'=>14, 'pct'=>85.71} }
|
67
|
+
|
68
|
+
raw = model.raw_for("lib/foo.rb")
|
69
|
+
# => { 'file' => '/abs/.../lib/foo.rb', 'lines' => [nil, 1, 0, 3, ...] }
|
70
|
+
|
71
|
+
uncovered = model.uncovered_for("lib/foo.rb")
|
72
|
+
# => { 'file' => '/abs/.../lib/foo.rb', 'uncovered' => [5, 9, 12], 'summary' => { ... } }
|
73
|
+
|
74
|
+
detailed = model.detailed_for("lib/foo.rb")
|
75
|
+
# => { 'file' => '/abs/.../lib/foo.rb', 'lines' => [{'line' => 1, 'hits' => 1, 'covered' => true}, ...], 'summary' => { ... } }
|
76
|
+
```
|
77
|
+
|
78
|
+
### MCP Quick Start
|
79
|
+
|
80
|
+
- Prereqs: Ruby 3.2+; run tests once so `coverage/.resultset.json` exists.
|
81
|
+
- One-off MCP requests can be made by piping JSON-RPC to the server:
|
82
|
+
|
83
|
+
**Important**: JSON-RPC messages must be on a single line (no line breaks). Multi-line JSON will cause parse errors.
|
84
|
+
|
85
|
+
```sh
|
86
|
+
# Per-file summary (staleness off)
|
87
|
+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_summary_tool","arguments":{"path":"lib/foo.rb","resultset":"coverage","stale":"off"}}}' | simplecov-mcp
|
88
|
+
|
89
|
+
# All files with project-level staleness checks
|
90
|
+
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"all_files_coverage_tool","arguments":{"resultset":"coverage","stale":"error","tracked_globs":["lib/**/*.rb"]}}}' | simplecov-mcp
|
91
|
+
```
|
92
|
+
|
93
|
+
Tip: In an MCP-capable editor/agent, configure `simplecov-mcp` as a stdio server and call the same tool names with the `arguments` shown above.
|
94
|
+
|
95
|
+
Resultset resolution:
|
96
|
+
|
97
|
+
- If `resultset:` is provided, it may be:
|
98
|
+
- a file path to `.resultset.json`, or
|
99
|
+
- a directory containing `.resultset.json` (e.g., `coverage/`).
|
100
|
+
- If `resultset:` is omitted, resolution follows:
|
101
|
+
1) `ENV["SIMPLECOV_RESULTSET"]` (file or directory), then
|
102
|
+
2) `.resultset.json`, 3) `coverage/.resultset.json`, 4) `tmp/.resultset.json` under `root`.
|
103
|
+
|
104
|
+
Path semantics:
|
105
|
+
|
106
|
+
- Methods accept paths relative to `root` or absolute paths. Internally, the model resolves to an absolute path and looks up coverage by:
|
107
|
+
1) exact absolute path,
|
108
|
+
2) the path without the current working directory prefix,
|
109
|
+
3) filename (basename) match as a last resort.
|
110
|
+
|
111
|
+
Sorting:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
model.all_files(sort_order: :descending) # or :ascending (default)
|
115
|
+
```
|
116
|
+
|
117
|
+
## Example Prompts
|
118
|
+
|
119
|
+
Using simplecov-mcp, show me a table of all files and their coverages.
|
120
|
+
|
121
|
+
----
|
122
|
+
|
123
|
+
Using simplecov-mcp, find the uncovered code lines and report to me:
|
124
|
+
|
125
|
+
* the most important coverage omissions to address
|
126
|
+
* the simplest coverage omissions to address
|
127
|
+
* analyze the risk of the current state of coverage
|
128
|
+
* propose a plan of action (if any) to improve coverage state
|
129
|
+
|
130
|
+
----
|
131
|
+
|
132
|
+
|
133
|
+
## Error Handling
|
134
|
+
|
135
|
+
This tool provides different error handling behavior depending on how it's used:
|
136
|
+
|
137
|
+
### CLI Mode
|
138
|
+
When used as a command-line tool, errors are displayed as user-friendly messages without stack traces:
|
139
|
+
|
140
|
+
```bash
|
141
|
+
$ simplecov-mcp summary nonexistent.rb
|
142
|
+
File error: No coverage data found for the specified file
|
143
|
+
```
|
144
|
+
|
145
|
+
For debugging, set the `SIMPLECOV_MCP_DEBUG=1` environment variable to see full stack traces.
|
146
|
+
|
147
|
+
### Library Mode
|
148
|
+
When used as a Ruby library, errors are raised as custom exception classes that can be caught and handled:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
begin
|
152
|
+
SimpleCovMcp.run_as_library(['summary', 'missing.rb'])
|
153
|
+
rescue SimpleCovMcp::FileError => e
|
154
|
+
puts "Handled gracefully: #{e.user_friendly_message}"
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
Available exception classes:
|
159
|
+
- `SimpleCovMcp::Error` - Base error class
|
160
|
+
- `SimpleCovMcp::FileError` - File not found or access issues
|
161
|
+
- `SimpleCovMcp::CoverageDataError` - Invalid or missing coverage data
|
162
|
+
- `SimpleCovMcp::ConfigurationError` - Configuration problems
|
163
|
+
- `SimpleCovMcp::UsageError` - Command usage errors
|
164
|
+
|
165
|
+
### MCP Server Mode
|
166
|
+
When running as an MCP server, errors are handled internally and returned as structured responses to the MCP client. The MCP server uses:
|
167
|
+
|
168
|
+
- **Logging enabled** - Errors are logged to `~/simplecov_mcp.log` for server debugging
|
169
|
+
- **Clean error messages** - User-friendly messages are returned to the client (no stack traces unless `SIMPLECOV_MCP_DEBUG=1`)
|
170
|
+
- **Structured responses** - Errors are returned as proper MCP tool responses, not exceptions
|
171
|
+
|
172
|
+
The MCP server automatically configures error handling appropriately for server usage.
|
173
|
+
|
174
|
+
### Custom Error Handlers
|
175
|
+
Library usage defaults to no logging to avoid side effects, but you can customize this:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
# Default library behavior - no logging
|
179
|
+
SimpleCovMcp.run_as_library(['summary', 'file.rb'])
|
180
|
+
|
181
|
+
# Custom error handler with logging enabled
|
182
|
+
handler = SimpleCovMcp::ErrorHandler.new(
|
183
|
+
log_errors: true, # Enable logging for library usage
|
184
|
+
show_stack_traces: false # Clean error messages
|
185
|
+
)
|
186
|
+
SimpleCovMcp.run_as_library(argv, error_handler: handler)
|
187
|
+
|
188
|
+
# Or configure globally for MCP tools
|
189
|
+
SimpleCovMcp.configure_error_handling do |handler|
|
190
|
+
handler.log_errors = true
|
191
|
+
handler.show_stack_traces = true # For debugging
|
192
|
+
end
|
193
|
+
```
|
23
194
|
|
24
|
-
|
195
|
+
### Legacy Error Handling Tips
|
25
196
|
|
26
|
-
|
197
|
+
- Missing resultset: `SimpleCov::Mcp::CovUtil.find_resultset` raises with guidance to run tests or set `SIMPLECOV_RESULTSET`.
|
198
|
+
- Missing file coverage: lookups raise `"No coverage entry found for <path>"`.
|
27
199
|
|
28
|
-
|
29
|
-
|
30
|
-
|
200
|
+
Example: fail CI if a file has uncovered lines
|
201
|
+
|
202
|
+
```ruby
|
203
|
+
require "simple_cov_mcp"
|
204
|
+
|
205
|
+
model = SimpleCovMcp::CoverageModel.new(root: Dir.pwd)
|
206
|
+
res = model.uncovered_for("lib/foo.rb")
|
207
|
+
if res['uncovered'].any?
|
208
|
+
warn "Uncovered lines in lib/foo.rb: #{res['uncovered'].join(", ")}"
|
209
|
+
exit 1
|
210
|
+
end
|
211
|
+
```
|
212
|
+
|
213
|
+
Example: enforce a project-wide minimum percentage
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
require "simple_cov_mcp"
|
217
|
+
|
218
|
+
threshold = 90.0
|
219
|
+
min = model.all_files.map { |r| r['percentage'] }.min || 100.0
|
220
|
+
if min < threshold
|
221
|
+
warn "Min coverage %.2f%% is below threshold %.2f%%" % [min, threshold]
|
222
|
+
exit 1
|
223
|
+
end
|
224
|
+
```
|
225
|
+
|
226
|
+
Public API stability:
|
227
|
+
|
228
|
+
- Consider the following public and stable under SemVer:
|
229
|
+
- `SimpleCovMcp::CoverageModel.new(root:, resultset:, staleness: 'off', tracked_globs: nil)`
|
230
|
+
- `#raw_for(path)`, `#summary_for(path)`, `#uncovered_for(path)`, `#detailed_for(path)`, `#all_files(sort_order:)`
|
231
|
+
- Return shapes shown above (keys and value types). For `all_files`, each row also includes `'stale' => true|false`.
|
232
|
+
- CLI (`SimpleCovMcp.run(argv)`) and MCP tools remain stable but are separate surfaces.
|
233
|
+
- Internal helpers under `SimpleCovMcp::CovUtil` may change; prefer `CoverageModel` unless you need low-level access.
|
234
|
+
|
235
|
+
### Resultset Location
|
236
|
+
|
237
|
+
- Defaults (search order):
|
238
|
+
1. `.resultset.json`
|
239
|
+
2. `coverage/.resultset.json`
|
240
|
+
3. `tmp/.resultset.json`
|
241
|
+
- Override via CLI: `--resultset PATH` (PATH may be the file itself or a directory containing `.resultset.json`).
|
242
|
+
- Override via environment: `SIMPLECOV_RESULTSET=PATH` (file or directory). This takes precedence over defaults. The CLI flag, when present, takes precedence over the environment variable.
|
31
243
|
|
32
244
|
### CLI Mode
|
33
245
|
|
246
|
+
### Stale Coverage Errors
|
247
|
+
|
248
|
+
When strict staleness checking is enabled, the model (and CLI) raise a
|
249
|
+
`CoverageDataStaleError` if a source file appears newer than the coverage data
|
250
|
+
or the line counts differ.
|
251
|
+
|
252
|
+
- Enable per instance: `SimpleCovMcp::CoverageModel.new(staleness: 'error')`
|
253
|
+
|
254
|
+
The error message is detailed and includes:
|
255
|
+
|
256
|
+
- File and Coverage times (UTC and local) and line counts
|
257
|
+
- A delta indicating how much newer the file is than coverage
|
258
|
+
- The absolute path to the `.resultset.json` used
|
259
|
+
|
260
|
+
Example excerpt:
|
261
|
+
|
262
|
+
```
|
263
|
+
Coverage data stale: Coverage data appears stale for lib/foo.rb
|
264
|
+
File - time: 2025-09-16T14:03:22Z (local 2025-09-16T07:03:22-07:00), lines: 226
|
265
|
+
Coverage - time: 2025-09-15T21:11:09Z (local 2025-09-15T14:11:09-07:00), lines: 220
|
266
|
+
Delta - file is +123s newer than coverage
|
267
|
+
Resultset - /path/to/your/project/coverage/.resultset.json
|
268
|
+
```
|
269
|
+
|
34
270
|
Run in a project directory with a SimpleCov resultset:
|
35
271
|
|
272
|
+
```sh
|
273
|
+
simplecov-mcp # same as 'list'
|
36
274
|
```
|
37
|
-
|
275
|
+
|
276
|
+
Subcommands:
|
277
|
+
|
278
|
+
- `list` — show the table of all files (sorted ascending by default)
|
279
|
+
- `summary <path>` — show covered/total/% for a file
|
280
|
+
- `raw <path>` — show the original SimpleCov lines array
|
281
|
+
- `uncovered <path>` — show uncovered lines and summary
|
282
|
+
- `detailed <path>` — show per-line rows with hits and covered
|
283
|
+
- `version` — show version information
|
284
|
+
|
285
|
+
Global flags (OptionParser):
|
286
|
+
|
287
|
+
- `--cli` (alias `--report`) — force CLI output
|
288
|
+
- `--resultset PATH` — path or directory for `.resultset.json`
|
289
|
+
- `--root PATH` — project root (default `.`)
|
290
|
+
- `--json` — print JSON output for machine use
|
291
|
+
- `--sort-order ascending|descending` — for `list`
|
292
|
+
- `--source[=MODE]` — include source text for `summary`, `uncovered`, `detailed` (MODE: `full` or `uncovered`; default `full`)
|
293
|
+
- `--source-context N` — for `--source=uncovered`, lines of context (default 2)
|
294
|
+
- `--color` / `--no-color` — enable/disable ANSI colors in source output
|
295
|
+
- `--stale off|error` — staleness checking mode (default `off`)
|
296
|
+
- `--tracked-globs x,y,z` — globs for files that should be covered (applies to `list` staleness only)
|
297
|
+
- `--help` — show usage
|
298
|
+
|
299
|
+
Select a nonstandard resultset path:
|
300
|
+
|
301
|
+
```sh
|
302
|
+
simplecov-mcp --cli --resultset build/coverage/.resultset.json
|
303
|
+
# or
|
304
|
+
SIMPLECOV_RESULTSET=build/coverage/.resultset.json simplecov-mcp --cli
|
38
305
|
```
|
39
306
|
|
40
|
-
|
307
|
+
You can also pass a directory that contains `.resultset.json` (common when the file lives in a `coverage/` folder):
|
41
308
|
|
309
|
+
```sh
|
310
|
+
simplecov-mcp --cli --resultset coverage
|
311
|
+
# or via env
|
312
|
+
SIMPLECOV_RESULTSET=coverage simplecov-mcp --cli
|
42
313
|
```
|
314
|
+
|
315
|
+
Forces CLI mode:
|
316
|
+
|
317
|
+
```sh
|
43
318
|
simplecov-mcp --cli
|
44
319
|
# or
|
45
|
-
|
46
|
-
|
320
|
+
SIMPLECOV_MCP_CLI=1 simplecov-mcp
|
321
|
+
```
|
47
322
|
|
48
323
|
Example output:
|
49
324
|
|
50
|
-
```
|
51
|
-
|
52
|
-
│ File │ % │ Covered │ Total │
|
53
|
-
|
54
|
-
│
|
55
|
-
│ lib/
|
56
|
-
│
|
57
|
-
|
325
|
+
```text
|
326
|
+
┌───────────────────────────┬──────────┬──────────┬────────┬───┐
|
327
|
+
│ File │ % │ Covered │ Total │ ! │
|
328
|
+
├───────────────────────────┼──────────┼──────────┼────────┼───┤
|
329
|
+
│ spec/user_spec.rb │ 85.71 │ 12 │ 14 │ │
|
330
|
+
│ lib/models/user.rb │ 92.31 │ 12 │ 13 │ ! │
|
331
|
+
│ lib/services/auth.rb │ 100.00 │ 8 │ 8 │ │
|
332
|
+
└───────────────────────────┴──────────┴──────────┴────────┴───┘
|
58
333
|
```
|
59
334
|
|
60
335
|
Files are sorted by percentage (ascending), then by path.
|
@@ -65,33 +340,105 @@ When stdin has data (e.g., from an MCP client), the program runs as an MCP serve
|
|
65
340
|
|
66
341
|
Available tools:
|
67
342
|
|
68
|
-
- `
|
69
|
-
- `
|
70
|
-
- `
|
71
|
-
- `
|
72
|
-
- `
|
343
|
+
- `coverage_raw_tool(path, root=".", resultset=nil, stale='off')`
|
344
|
+
- `coverage_summary_tool(path, root=".", resultset=nil, stale='off')`
|
345
|
+
- `uncovered_lines_tool(path, root=".", resultset=nil, stale='off')`
|
346
|
+
- `coverage_detailed_tool(path, root=".", resultset=nil, stale='off')`
|
347
|
+
- `all_files_coverage_tool(root=".", resultset=nil, stale='off', tracked_globs=nil)`
|
348
|
+
- Returns `{ files: [{"file","covered","total","percentage","stale"}, ...] }` where `stale` is a boolean.
|
349
|
+
- `version_tool()` — returns version information
|
73
350
|
|
74
|
-
|
351
|
+
Notes:
|
75
352
|
|
353
|
+
- `resultset` lets clients pass a nonstandard path to `.resultset.json` directly (absolute or relative to `root`). It may be:
|
354
|
+
- a file path to `.resultset.json`, or
|
355
|
+
- a directory path containing `.resultset.json` (e.g., `coverage/`).
|
356
|
+
- If `resultset` is omitted, the server checks `SIMPLECOV_RESULTSET`, then searches `.resultset.json`, `coverage/.resultset.json`, `tmp/.resultset.json` in that order.
|
357
|
+
- `stale` controls staleness checking per call (`off` or `error`).
|
358
|
+
- For `all_files_coverage`, `tracked_globs` detects new project files missing from coverage, and the tool also flags covered files that are newer than the coverage timestamp or present in coverage but deleted in the project.
|
359
|
+
|
360
|
+
Example (manual - note single-line JSON-RPC format):
|
361
|
+
|
362
|
+
```sh
|
363
|
+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_summary_tool","arguments":{"path":"lib/foo.rb"}}}' | simplecov-mcp
|
76
364
|
```
|
77
|
-
|
365
|
+
|
366
|
+
With an explicit resultset path:
|
367
|
+
|
368
|
+
```sh
|
369
|
+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_summary_tool","arguments":{"path":"lib/foo.rb","resultset":"build/coverage/.resultset.json"}}}' | simplecov-mcp
|
370
|
+
```
|
371
|
+
|
372
|
+
With a resultset directory:
|
373
|
+
|
374
|
+
```sh
|
375
|
+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_summary_tool","arguments":{"path":"lib/foo.rb","resultset":"coverage"}}}' | simplecov-mcp
|
78
376
|
```
|
79
377
|
|
378
|
+
CLI vs MCP summary:
|
379
|
+
|
380
|
+
- CLI: use subcommands. Pass `--resultset PATH` or set `SIMPLECOV_RESULTSET`.
|
381
|
+
- MCP: pass `resultset` in tool arguments, or set `SIMPLECOV_RESULTSET`.
|
382
|
+
|
383
|
+
## Troubleshooting
|
384
|
+
|
385
|
+
- MCP client fails to start or times out
|
386
|
+
- Likely cause: launching `simplecov-mcp` with an older Ruby that cannot load the `mcp` gem. This gem requires Ruby >= 3.2.
|
387
|
+
- Check the Ruby your MCP client uses: run `ruby -v` in the same environment your client inherits; ensure it reports 3.2+.
|
388
|
+
- Fix PATH or select a newer Ruby via rbenv/rvm/asdf, then retry. You can configure your MCP client to point to the shim/binary for that Ruby version. For example:
|
389
|
+
- rbenv shim: `~/.rbenv/shims/simplecov-mcp`
|
390
|
+
- asdf shim: `~/.asdf/shims/simplecov-mcp`
|
391
|
+
- RVM wrapper: `/Users/you/.rvm/wrappers/ruby-3.3.0/simplecov-mcp` (adjust version)
|
392
|
+
- Codex CLI example (`~/.codex/config.toml`):
|
393
|
+
```toml
|
394
|
+
# Use the Ruby 3.2+ shim for the MCP server
|
395
|
+
[tools.simplecov_mcp]
|
396
|
+
command = "/Users/you/.rbenv/shims/simplecov-mcp"
|
397
|
+
cwd = "/path/to/your/project"
|
398
|
+
```
|
399
|
+
- Validate manually: `simplecov-mcp --cli` (or from this repo: `ruby -Ilib exe/simplecov-mcp --cli`). If you see the coverage table, the binary starts correctly.
|
400
|
+
- On failures, check `~/simplecov_mcp.log` for details.
|
401
|
+
|
80
402
|
### Notes
|
81
403
|
|
82
|
-
- Library entrypoint: `require "
|
83
|
-
- Programmatic run: `
|
84
|
-
-
|
404
|
+
- Library entrypoint: `require "simple_cov_mcp"` (also `simplecov_mcp`). Legacy `simple_cov/mcp` is supported.
|
405
|
+
- Programmatic run: `SimpleCovMcp.run(ARGV)`
|
406
|
+
- Staleness checks: pass `staleness: 'error'` to `CoverageModel` (or use CLI `--stale error`) to
|
407
|
+
raise if source mtimes are newer than coverage or line counts mismatch. Use
|
408
|
+
`--tracked-globs` (CLI) or `tracked_globs` (API/MCP) to flag new files.
|
409
|
+
- Logs basic diagnostics to `~/simplecov_mcp.log`.
|
410
|
+
|
411
|
+
## Executables and PATH
|
412
|
+
|
413
|
+
To run `simplecov-mcp` globally, your PATH must include where Ruby installs executables.
|
414
|
+
|
415
|
+
- Version managers
|
416
|
+
- RVM, rbenv, asdf, chruby typically add the right bin/shim directories to PATH.
|
417
|
+
- Ensure your shell is initialized (e.g., rbenv init, asdf reshim ruby after installs).
|
418
|
+
- Without a manager
|
419
|
+
- Add the gem bin dir to PATH: see it with `gem env` (look for "EXECUTABLE DIRECTORY") or `ruby -e 'puts Gem.bindir'`.
|
420
|
+
- Example: `export PATH="$HOME/.gem/ruby/3.2.0/bin:$PATH"` (adjust version).
|
421
|
+
- Alternatives
|
422
|
+
- Use Bundler: `bundle exec simplecov-mcp` with cwd set to your project.
|
423
|
+
- Symlink the repo executable into a bin on your PATH; the script resolves its lib/ via realpath.
|
424
|
+
- Or configure Codex to run the executable by filename (`simplecov-mcp`) and inherit the current workspace as cwd.
|
85
425
|
|
86
426
|
## Development
|
87
427
|
|
88
428
|
Standard Ruby gem structure. After cloning:
|
89
429
|
|
90
|
-
```
|
430
|
+
```sh
|
91
431
|
bundle install
|
92
432
|
ruby -Ilib exe/simplecov-mcp --cli
|
93
433
|
```
|
94
434
|
|
435
|
+
Run tests with coverage (SimpleCov writes to `coverage/`):
|
436
|
+
|
437
|
+
```sh
|
438
|
+
bundle exec rspec
|
439
|
+
# open coverage/index.html for HTML report, or run exe/simplecov-mcp for table summary
|
440
|
+
```
|
441
|
+
|
95
442
|
## License
|
96
443
|
|
97
444
|
MIT
|
data/exe/simplecov-mcp
CHANGED
@@ -1,6 +1,23 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
# Make it safe to run via symlink by resolving the real path
|
5
|
+
root = File.expand_path("..", File.realpath(__FILE__))
|
6
|
+
lib = File.join(root, "lib")
|
7
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
8
|
|
6
|
-
|
9
|
+
# Fail fast with a helpful message if Ruby is too old for this gem
|
10
|
+
begin
|
11
|
+
require 'rubygems'
|
12
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.2')
|
13
|
+
$stderr.puts "simplecov-mcp requires Ruby >= 3.2 (current: #{RUBY_VERSION})."
|
14
|
+
$stderr.puts "Please run with a supported Ruby version (e.g., via rbenv, rvm, asdf)."
|
15
|
+
exit 1
|
16
|
+
end
|
17
|
+
rescue StandardError
|
18
|
+
# If anything goes wrong, let the app load and potentially fail with a more specific error.
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'simple_cov_mcp'
|
22
|
+
|
23
|
+
SimpleCovMcp.run(ARGV)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mcp'
|
4
|
+
require_relative 'errors'
|
5
|
+
require_relative 'error_handler'
|
6
|
+
|
7
|
+
module SimpleCovMcp
|
8
|
+
class BaseTool < ::MCP::Tool
|
9
|
+
INPUT_SCHEMA = {
|
10
|
+
type: 'object',
|
11
|
+
additionalProperties: false,
|
12
|
+
properties: {
|
13
|
+
path: {
|
14
|
+
type: 'string',
|
15
|
+
description: 'Repo-relative or absolute path to the file whose coverage data you need.',
|
16
|
+
examples: ['lib/simple_cov_mcp/model.rb']
|
17
|
+
},
|
18
|
+
root: {
|
19
|
+
type: 'string',
|
20
|
+
description: 'Project root used to resolve relative paths (defaults to current workspace).',
|
21
|
+
default: '.'
|
22
|
+
},
|
23
|
+
resultset: {
|
24
|
+
type: 'string',
|
25
|
+
description: 'Path to the SimpleCov .resultset.json file (absolute or relative to root).'
|
26
|
+
},
|
27
|
+
stale: {
|
28
|
+
type: 'string',
|
29
|
+
description: "How to handle missing/outdated coverage data. 'off' skips checks; 'error' raises.",
|
30
|
+
enum: %w[off error],
|
31
|
+
default: 'off'
|
32
|
+
}
|
33
|
+
},
|
34
|
+
required: ['path']
|
35
|
+
}
|
36
|
+
def self.input_schema_def = INPUT_SCHEMA
|
37
|
+
|
38
|
+
# Handle errors consistently across all MCP tools
|
39
|
+
# Returns an MCP::Tool::Response with appropriate error message
|
40
|
+
def self.handle_mcp_error(error, tool_name)
|
41
|
+
# Normalize to a SimpleCovMcp::Error so we can handle/log uniformly
|
42
|
+
normalized = error.is_a?(SimpleCovMcp::Error) ? error : SimpleCovMcp.error_handler.convert_standard_error(error)
|
43
|
+
log_mcp_error(normalized, tool_name)
|
44
|
+
::MCP::Tool::Response.new([{ type: 'text', text: "Error: #{normalized.user_friendly_message}" }])
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def self.log_mcp_error(error, tool_name)
|
50
|
+
# Access the error handler's log_error method via send to bypass visibility
|
51
|
+
SimpleCovMcp.error_handler.send(:log_error, error, tool_name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|