simplecov-mcp 0.2.1 → 0.3.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 +12 -1
- data/lib/simple_cov_mcp/base_tool.rb +16 -0
- data/lib/simple_cov_mcp/tools/all_files_coverage_tool.rb +1 -3
- data/lib/simple_cov_mcp/tools/coverage_detailed_tool.rb +1 -1
- data/lib/simple_cov_mcp/tools/coverage_raw_tool.rb +1 -1
- data/lib/simple_cov_mcp/tools/coverage_summary_tool.rb +1 -1
- data/lib/simple_cov_mcp/tools/help_tool.rb +1 -3
- data/lib/simple_cov_mcp/tools/uncovered_lines_tool.rb +1 -1
- data/lib/simple_cov_mcp/version.rb +1 -1
- data/spec/all_files_coverage_tool_spec.rb +3 -1
- data/spec/coverage_detailed_tool_spec.rb +36 -0
- data/spec/coverage_raw_tool_spec.rb +32 -0
- data/spec/coverage_summary_tool_spec.rb +39 -0
- data/spec/help_tool_spec.rb +5 -4
- data/spec/uncovered_lines_tool_spec.rb +33 -0
- data/spec/version_spec.rb +6 -1
- data/spec/version_tool_spec.rb +22 -0
- metadata +6 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08e9a29ca4d2e2daafd4620690021de136eaa2cfc2ba652340f73d8b69547d8d'
|
4
|
+
data.tar.gz: 784e8ef1f21e6ed4e37783a840935e909d8a6d1f6ae4c941da34d0e6635f9f95
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52e2a95fda335f611f38cd0b97164a62e381532ceb71b330c32023be10eeb8d97e999bdc32f0dafac168f0470cac481151e78f223623e90a377fe79ef807705b
|
7
|
+
data.tar.gz: 42ce8d9f01b069f2e3dd0d0e817cd135298fa14d06553ef5b0771d37baf9f5bd4c194d9a9296a823966da21ec505e69b41fd489dfcfa1b0e0b74bb63949b7a21
|
data/README.md
CHANGED
@@ -92,6 +92,12 @@ echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"all_files_
|
|
92
92
|
|
93
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
94
|
|
95
|
+
Response content types (MCP):
|
96
|
+
|
97
|
+
- JSON data returns as a single `type: "resource"` item with `resource.mimeType: "application/json"` and the JSON string in `resource.text` (e.g., `name: "coverage_summary.json"`).
|
98
|
+
- Human-readable strings (e.g., the table and version) return as `type: "text"`.
|
99
|
+
- Errors return as `type: "text"` with a friendly message.
|
100
|
+
|
95
101
|
Resultset resolution:
|
96
102
|
|
97
103
|
- If `resultset:` is provided, it may be:
|
@@ -120,7 +126,7 @@ Using simplecov-mcp, show me a table of all files and their coverages.
|
|
120
126
|
|
121
127
|
----
|
122
128
|
|
123
|
-
Using simplecov-mcp, find the uncovered code lines and report to me:
|
129
|
+
Using simplecov-mcp, find the uncovered code lines and report to me, in a markdown file:
|
124
130
|
|
125
131
|
* the most important coverage omissions to address
|
126
132
|
* the simplest coverage omissions to address
|
@@ -348,6 +354,11 @@ Available tools:
|
|
348
354
|
- Returns `{ files: [{"file","covered","total","percentage","stale"}, ...] }` where `stale` is a boolean.
|
349
355
|
- `version_tool()` — returns version information
|
350
356
|
|
357
|
+
Response shape and content types:
|
358
|
+
|
359
|
+
- JSON tools above return a single content item `{ "type": "resource", "resource": { "mimeType": "application/json", "text": "{...}", "name": "<tool>.json" } }`.
|
360
|
+
- `coverage_table_tool` and `version_tool` return `{ "type": "text", "text": "..." }`.
|
361
|
+
|
351
362
|
Notes:
|
352
363
|
|
353
364
|
- `resultset` lets clients pass a nonstandard path to `.resultset.json` directly (absolute or relative to `root`). It may be:
|
@@ -44,6 +44,22 @@ module SimpleCovMcp
|
|
44
44
|
::MCP::Tool::Response.new([{ type: 'text', text: "Error: #{normalized.user_friendly_message}" }])
|
45
45
|
end
|
46
46
|
|
47
|
+
# Respond with JSON as a resource to avoid clients mutating content types.
|
48
|
+
# The resource embeds the JSON string with a clear MIME type.
|
49
|
+
def self.respond_json(payload, name: 'data.json', pretty: false)
|
50
|
+
json = pretty ? JSON.pretty_generate(payload) : JSON.generate(payload)
|
51
|
+
::MCP::Tool::Response.new([
|
52
|
+
{
|
53
|
+
'type' => 'resource',
|
54
|
+
'resource' => {
|
55
|
+
'mimeType' => 'application/json',
|
56
|
+
'text' => json,
|
57
|
+
'name' => name
|
58
|
+
}
|
59
|
+
}
|
60
|
+
])
|
61
|
+
end
|
62
|
+
|
47
63
|
private
|
48
64
|
|
49
65
|
def self.log_mcp_error(error, tool_name)
|
@@ -53,9 +53,7 @@ module SimpleCovMcp
|
|
53
53
|
stale_count = files.count { |f| f['stale'] }
|
54
54
|
ok_count = total - stale_count
|
55
55
|
payload = { files: files, counts: { total: total, ok: ok_count, stale: stale_count } }
|
56
|
-
|
57
|
-
{ 'type' => 'text', 'text' => JSON.generate(payload) }
|
58
|
-
])
|
56
|
+
respond_json(payload, name: 'all_files_coverage.json')
|
59
57
|
rescue => e
|
60
58
|
handle_mcp_error(e, 'AllFilesCoverageTool')
|
61
59
|
end
|
@@ -19,7 +19,7 @@ module SimpleCovMcp
|
|
19
19
|
mode = stale
|
20
20
|
model = CoverageModel.new(root: root, resultset: resultset, staleness: mode)
|
21
21
|
data = model.detailed_for(path)
|
22
|
-
|
22
|
+
respond_json(data, name: 'coverage_detailed.json', pretty: true)
|
23
23
|
rescue => e
|
24
24
|
handle_mcp_error(e, 'CoverageDetailedTool')
|
25
25
|
end
|
@@ -19,7 +19,7 @@ module SimpleCovMcp
|
|
19
19
|
mode = stale
|
20
20
|
model = CoverageModel.new(root: root, resultset: resultset, staleness: mode)
|
21
21
|
data = model.raw_for(path)
|
22
|
-
|
22
|
+
respond_json(data, name: 'coverage_raw.json', pretty: true)
|
23
23
|
rescue => e
|
24
24
|
handle_mcp_error(e, 'CoverageRawTool')
|
25
25
|
end
|
@@ -19,7 +19,7 @@ module SimpleCovMcp
|
|
19
19
|
mode = stale
|
20
20
|
model = CoverageModel.new(root: root, resultset: resultset, staleness: mode)
|
21
21
|
data = model.summary_for(path)
|
22
|
-
|
22
|
+
respond_json(data, name: 'coverage_summary.json', pretty: true)
|
23
23
|
rescue => e
|
24
24
|
handle_mcp_error(e, 'CoverageSummaryTool')
|
25
25
|
end
|
@@ -89,9 +89,7 @@ module SimpleCovMcp
|
|
89
89
|
entries = filter_entries(entries, query) if query && !query.strip.empty?
|
90
90
|
|
91
91
|
data = { query: query, tools: entries }
|
92
|
-
|
93
|
-
{ 'type' => 'text', 'text' => JSON.generate(data) }
|
94
|
-
])
|
92
|
+
respond_json(data, name: 'tools_help.json')
|
95
93
|
rescue => e
|
96
94
|
handle_mcp_error(e, 'HelpTool')
|
97
95
|
end
|
@@ -19,7 +19,7 @@ module SimpleCovMcp
|
|
19
19
|
mode = stale
|
20
20
|
model = CoverageModel.new(root: root, resultset: resultset, staleness: mode)
|
21
21
|
data = model.uncovered_for(path)
|
22
|
-
|
22
|
+
respond_json(data, name: 'uncovered_lines.json', pretty: true)
|
23
23
|
rescue => e
|
24
24
|
handle_mcp_error(e, 'UncoveredLinesTool')
|
25
25
|
end
|
@@ -28,7 +28,9 @@ RSpec.describe SimpleCovMcp::Tools::AllFilesCoverageTool do
|
|
28
28
|
|
29
29
|
response = described_class.call(root: root, server_context: server_context)
|
30
30
|
entry = response.payload.first
|
31
|
-
|
31
|
+
expect(entry['type']).to eq('resource')
|
32
|
+
expect(entry['resource']).to include('mimeType' => 'application/json')
|
33
|
+
json = JSON.parse(entry['resource']['text'])
|
32
34
|
|
33
35
|
expect(json).to have_key('files')
|
34
36
|
files = json['files']
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'simple_cov_mcp/tools/coverage_detailed_tool'
|
5
|
+
|
6
|
+
RSpec.describe SimpleCovMcp::Tools::CoverageDetailedTool do
|
7
|
+
let(:server_context) { instance_double('ServerContext').as_null_object }
|
8
|
+
|
9
|
+
before do
|
10
|
+
stub_const('MCP::Tool::Response', Struct.new(:payload))
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns JSON as an application/json resource' do
|
14
|
+
model = instance_double(SimpleCovMcp::CoverageModel)
|
15
|
+
allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
|
16
|
+
allow(model).to receive(:detailed_for).with('lib/foo.rb').and_return(
|
17
|
+
{
|
18
|
+
'file' => '/abs/path/lib/foo.rb',
|
19
|
+
'lines' => [
|
20
|
+
{ 'line' => 1, 'hits' => 1, 'covered' => true },
|
21
|
+
{ 'line' => 2, 'hits' => 0, 'covered' => false }
|
22
|
+
],
|
23
|
+
'summary' => { 'covered' => 1, 'total' => 2, 'pct' => 50.0 }
|
24
|
+
}
|
25
|
+
)
|
26
|
+
|
27
|
+
response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
|
28
|
+
item = response.payload.first
|
29
|
+
expect(item['type']).to eq('resource')
|
30
|
+
expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'coverage_detailed.json')
|
31
|
+
data = JSON.parse(item['resource']['text'])
|
32
|
+
expect(data).to include('file', 'lines', 'summary')
|
33
|
+
expect(data['lines']).to be_an(Array)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'simple_cov_mcp/tools/coverage_raw_tool'
|
5
|
+
|
6
|
+
RSpec.describe SimpleCovMcp::Tools::CoverageRawTool do
|
7
|
+
let(:server_context) { instance_double('ServerContext').as_null_object }
|
8
|
+
|
9
|
+
before do
|
10
|
+
stub_const('MCP::Tool::Response', Struct.new(:payload))
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns JSON as an application/json resource' do
|
14
|
+
model = instance_double(SimpleCovMcp::CoverageModel)
|
15
|
+
allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
|
16
|
+
allow(model).to receive(:raw_for).with('lib/foo.rb').and_return(
|
17
|
+
{
|
18
|
+
'file' => '/abs/path/lib/foo.rb',
|
19
|
+
'lines' => [nil, 1, 0]
|
20
|
+
}
|
21
|
+
)
|
22
|
+
|
23
|
+
response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
|
24
|
+
item = response.payload.first
|
25
|
+
expect(item['type']).to eq('resource')
|
26
|
+
expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'coverage_raw.json')
|
27
|
+
data = JSON.parse(item['resource']['text'])
|
28
|
+
expect(data).to include('file', 'lines')
|
29
|
+
expect(data['lines']).to be_an(Array)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'simple_cov_mcp/tools/coverage_summary_tool'
|
5
|
+
|
6
|
+
RSpec.describe SimpleCovMcp::Tools::CoverageSummaryTool do
|
7
|
+
let(:server_context) { instance_double('ServerContext').as_null_object }
|
8
|
+
|
9
|
+
before do
|
10
|
+
response_class = Class.new do
|
11
|
+
attr_reader :payload, :meta
|
12
|
+
def initialize(payload, meta: nil)
|
13
|
+
@payload = payload
|
14
|
+
@meta = meta
|
15
|
+
end
|
16
|
+
end
|
17
|
+
stub_const('MCP::Tool::Response', response_class)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'returns JSON as an application/json resource' do
|
21
|
+
model = instance_double(SimpleCovMcp::CoverageModel)
|
22
|
+
allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
|
23
|
+
allow(model).to receive(:summary_for).with('lib/foo.rb').and_return(
|
24
|
+
{
|
25
|
+
'file' => '/abs/path/lib/foo.rb',
|
26
|
+
'summary' => { 'covered' => 10, 'total' => 12, 'pct' => 83.33 }
|
27
|
+
}
|
28
|
+
)
|
29
|
+
|
30
|
+
response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
|
31
|
+
item = response.payload.first
|
32
|
+
expect(item['type']).to eq('resource')
|
33
|
+
expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'coverage_summary.json')
|
34
|
+
data = JSON.parse(item['resource']['text'])
|
35
|
+
expect(data).to include('file', 'summary')
|
36
|
+
expect(data['summary']).to include('covered', 'total', 'pct')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
data/spec/help_tool_spec.rb
CHANGED
@@ -23,9 +23,9 @@ RSpec.describe SimpleCovMcp::Tools::HelpTool do
|
|
23
23
|
expect(response.meta).to be_nil
|
24
24
|
|
25
25
|
payload = response.payload.first
|
26
|
-
expect(payload['type']).to eq('
|
27
|
-
|
28
|
-
data = JSON.parse(payload['text'])
|
26
|
+
expect(payload['type']).to eq('resource')
|
27
|
+
expect(payload['resource']).to include('mimeType' => 'application/json')
|
28
|
+
data = JSON.parse(payload['resource']['text'])
|
29
29
|
tool_names = data['tools'].map { |entry| entry['tool'] }
|
30
30
|
|
31
31
|
expect(tool_names).to include('coverage_summary_tool', 'uncovered_lines_tool', 'all_files_coverage_tool', 'coverage_table_tool', 'version_tool')
|
@@ -35,7 +35,8 @@ RSpec.describe SimpleCovMcp::Tools::HelpTool do
|
|
35
35
|
it 'filters entries when a query is provided' do
|
36
36
|
response = described_class.call(query: 'uncovered', server_context: server_context)
|
37
37
|
payload = response.payload.first
|
38
|
-
|
38
|
+
expect(payload['type']).to eq('resource')
|
39
|
+
data = JSON.parse(payload['resource']['text'])
|
39
40
|
|
40
41
|
expect(data['tools']).not_to be_empty
|
41
42
|
expect(data['tools']).to all(satisfy do |entry|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'simple_cov_mcp/tools/uncovered_lines_tool'
|
5
|
+
|
6
|
+
RSpec.describe SimpleCovMcp::Tools::UncoveredLinesTool do
|
7
|
+
let(:server_context) { instance_double('ServerContext').as_null_object }
|
8
|
+
|
9
|
+
before do
|
10
|
+
stub_const('MCP::Tool::Response', Struct.new(:payload))
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns JSON as an application/json resource' do
|
14
|
+
model = instance_double(SimpleCovMcp::CoverageModel)
|
15
|
+
allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
|
16
|
+
allow(model).to receive(:uncovered_for).with('lib/foo.rb').and_return(
|
17
|
+
{
|
18
|
+
'file' => '/abs/path/lib/foo.rb',
|
19
|
+
'uncovered' => [5, 9, 12],
|
20
|
+
'summary' => { 'covered' => 10, 'total' => 12, 'pct' => 83.33 }
|
21
|
+
}
|
22
|
+
)
|
23
|
+
|
24
|
+
response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
|
25
|
+
item = response.payload.first
|
26
|
+
expect(item['type']).to eq('resource')
|
27
|
+
expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'uncovered_lines.json')
|
28
|
+
data = JSON.parse(item['resource']['text'])
|
29
|
+
expect(data).to include('file', 'uncovered', 'summary')
|
30
|
+
expect(data['uncovered']).to eq([5, 9, 12])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
data/spec/version_spec.rb
CHANGED
@@ -5,6 +5,11 @@ require 'spec_helper'
|
|
5
5
|
RSpec.describe 'Version constant' do
|
6
6
|
it 'exposes a semver-like version string' do
|
7
7
|
expect(SimpleCovMcp::VERSION).to be_a(String)
|
8
|
-
|
8
|
+
# Named fragments for readability (simplified SemVer)
|
9
|
+
CORE = /\d+\.\d+\.\d+/
|
10
|
+
ID = /[[:alnum:].-]+/ # ASCII alnum plus dot/hyphen
|
11
|
+
SEMVER = /\A#{CORE.source}(?:-#{ID.source})?(?:\+#{ID.source})?\z/
|
12
|
+
|
13
|
+
expect(SimpleCovMcp::VERSION).to match(SEMVER)
|
9
14
|
end
|
10
15
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'simple_cov_mcp/tools/version_tool'
|
5
|
+
|
6
|
+
RSpec.describe SimpleCovMcp::Tools::VersionTool do
|
7
|
+
let(:server_context) { instance_double('ServerContext').as_null_object }
|
8
|
+
|
9
|
+
before do
|
10
|
+
stub_const('MCP::Tool::Response', Struct.new(:payload))
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns a text payload with the version string' do
|
14
|
+
response = described_class.call(server_context: server_context)
|
15
|
+
item = response.payload.first
|
16
|
+
expect(item[:type] || item['type']).to eq('text')
|
17
|
+
text = item[:text] || item['text']
|
18
|
+
expect(text).to include('SimpleCovMcp version:')
|
19
|
+
expect(text).to include(SimpleCovMcp::VERSION)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simplecov-mcp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keith R. Bennett
|
@@ -110,6 +110,9 @@ files:
|
|
110
110
|
- spec/cli_spec.rb
|
111
111
|
- spec/cli_table_spec.rb
|
112
112
|
- spec/cli_usage_spec.rb
|
113
|
+
- spec/coverage_detailed_tool_spec.rb
|
114
|
+
- spec/coverage_raw_tool_spec.rb
|
115
|
+
- spec/coverage_summary_tool_spec.rb
|
113
116
|
- spec/coverage_table_tool_spec.rb
|
114
117
|
- spec/error_handler_spec.rb
|
115
118
|
- spec/errors_stale_spec.rb
|
@@ -122,8 +125,10 @@ files:
|
|
122
125
|
- spec/simplecov_mcp_model_spec.rb
|
123
126
|
- spec/spec_helper.rb
|
124
127
|
- spec/staleness_more_spec.rb
|
128
|
+
- spec/uncovered_lines_tool_spec.rb
|
125
129
|
- spec/util_spec.rb
|
126
130
|
- spec/version_spec.rb
|
131
|
+
- spec/version_tool_spec.rb
|
127
132
|
homepage: https://github.com/keithrbennett/simplecov-mcp
|
128
133
|
licenses:
|
129
134
|
- MIT
|