rubygems_mcp 0.1.3 → 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/CHANGELOG.md +25 -1
- data/README.md +39 -10
- data/lib/rubygems_mcp/cache.rb +76 -0
- data/lib/rubygems_mcp/client.rb +344 -177
- data/lib/rubygems_mcp/network_policy.rb +68 -0
- data/lib/rubygems_mcp/server.rb +127 -93
- data/lib/rubygems_mcp/version.rb +1 -1
- data/sig/rubygems_mcp.rbs +28 -9
- metadata +101 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 792f4e1e9055f0daca5acf09de1b71cdaa64a203808f9f71463d5cb7d67544e1
|
|
4
|
+
data.tar.gz: cc8a4b2af63e9b96534aae1aee6aeeb572fee2fd602d1d648d29b50c391fb9e5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5383415d5d7963b119273f486dca440c5af64e490065abfeaab9e3f9d21e03297cf3f0931252909f8fa2ba9d7e777c11101dcf483840ad70a9f1a6a3a96db816
|
|
7
|
+
data.tar.gz: ba60b3179ad4956457c4cd22e758e3a93a4f9ce4347697c899459003fc4002555077d8d56c0af525c7f0f8d74010fe5148c1ae4e6fb88d2b611e4f2177d28481
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.2.0 (2026-09-08)
|
|
6
|
+
|
|
7
|
+
- BREAKING: Require Ruby 3.4
|
|
8
|
+
- BREAKING: Raise `fast-mcp` to `>= 1.6` and `nokogiri` to `~> 1.19`
|
|
9
|
+
- BREAKING: `search_gems` paginates with `limit` and `offset` on the first search page
|
|
10
|
+
- BREAKING: Raise when an MCP version tool receives an invalid sort order
|
|
11
|
+
- Fetch gem changelogs only over HTTPS from GitHub, GitLab, RubyGems, and ruby-lang.org hosts
|
|
12
|
+
- Include the changelog source host on fetch results
|
|
13
|
+
- Read latest gem versions from gem info and keep rows whose dates fail to parse
|
|
14
|
+
- Skip caching empty Ruby changelogs after a GitHub fetch failure
|
|
15
|
+
- Stop reading HTTP bodies whose Content-Length exceeds 5MB
|
|
16
|
+
- Cap the in-memory response cache
|
|
17
|
+
- Paginate and cache the Ruby versions list on first fetch
|
|
18
|
+
- Apply the HTTP size limit to GitHub Ruby changelogs
|
|
19
|
+
- Limit latest-version lookups to 20 gem names
|
|
20
|
+
|
|
21
|
+
## 0.1.4 (2025-11-26)
|
|
22
|
+
|
|
23
|
+
- Add `get_news_releases` tool to fetch all new gem releases from RubyGems.org with pagination (fetches from `/news?page=N`)
|
|
24
|
+
- Add `get_popular_releases` tool to fetch popular new gem releases from RubyGems.org with pagination (fetches from `/releases/popular?page=N`)
|
|
25
|
+
- Add pagination support for `search_gems` tool via `page` parameter (converts page to offset automatically)
|
|
26
|
+
- Remove old patch code: unused monkey patches for fast-mcp `send_error`
|
|
27
|
+
|
|
3
28
|
## 0.1.3 (2025-11-21)
|
|
4
29
|
|
|
5
30
|
- Add Ruby roadmap tools: `get_ruby_roadmap` and `get_ruby_version_roadmap_details` for accessing Ruby version planning information from bugs.ruby-lang.org
|
|
@@ -53,4 +78,3 @@
|
|
|
53
78
|
- Comprehensive test suite with RSpec, VCR cassettes, and WebMock
|
|
54
79
|
- Requires Ruby 3.1 or higher
|
|
55
80
|
- All dependencies use latest compatible versions with pessimistic versioning for security
|
|
56
|
-
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# rubygems_mcp
|
|
2
2
|
|
|
3
|
-
[](https://badge.fury.io/rb/rubygems_mcp) [](https://github.com/amkisko/rubygems_mcp.rb/actions/workflows/test.yml) [](https://app.codecov.io/github/amkisko/rubygems_mcp.rb) [](https://sonarcloud.io/project/overview?id=amkisko_rubygems_mcp.rb)
|
|
4
4
|
|
|
5
5
|
Ruby gem providing RubyGems and Ruby version information via MCP (Model Context Protocol) server tools. Integrates with MCP-compatible clients like Cursor IDE, Claude Desktop, and other MCP-enabled tools.
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ Sponsored by [Kisko Labs](https://www.kiskolabs.com).
|
|
|
14
14
|
|
|
15
15
|
## Requirements
|
|
16
16
|
|
|
17
|
-
- **Ruby 3.
|
|
17
|
+
- **Ruby 3.4 or higher**
|
|
18
18
|
|
|
19
19
|
## Quick Start
|
|
20
20
|
|
|
@@ -30,12 +30,18 @@ For Cursor IDE, create or update `.cursor/mcp.json` in your project:
|
|
|
30
30
|
{
|
|
31
31
|
"mcpServers": {
|
|
32
32
|
"rubygems": {
|
|
33
|
-
"command": "
|
|
33
|
+
"command": "gem",
|
|
34
|
+
"args": ["exec", "rubygems_mcp"],
|
|
35
|
+
"env": {
|
|
36
|
+
"RUBY_VERSION": "3.4.7"
|
|
37
|
+
}
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
40
|
}
|
|
37
41
|
```
|
|
38
42
|
|
|
43
|
+
**Note**: Using `gem exec` ensures the correct Ruby version is used. If you're using a Ruby version manager like [mise](https://mise.jdx.dev/) or [rbenv](https://github.com/rbenv/rbenv), set the `RUBY_VERSION` environment variable to match your desired Ruby version. The `gem exec` command will automatically use the correct Ruby version based on your version manager configuration.
|
|
44
|
+
|
|
39
45
|
### Claude Desktop Configuration
|
|
40
46
|
|
|
41
47
|
For Claude Desktop, edit the MCP configuration file:
|
|
@@ -47,7 +53,11 @@ For Claude Desktop, edit the MCP configuration file:
|
|
|
47
53
|
{
|
|
48
54
|
"mcpServers": {
|
|
49
55
|
"rubygems": {
|
|
50
|
-
"command": "
|
|
56
|
+
"command": "gem",
|
|
57
|
+
"args": ["exec", "rubygems_mcp"],
|
|
58
|
+
"env": {
|
|
59
|
+
"RUBY_VERSION": "3.4.7"
|
|
60
|
+
}
|
|
51
61
|
}
|
|
52
62
|
}
|
|
53
63
|
}
|
|
@@ -93,7 +103,7 @@ The server will start and communicate via STDIN/STDOUT using the MCP protocol.
|
|
|
93
103
|
|
|
94
104
|
- **RubyGems API Client**: Full-featured client for RubyGems REST API with comprehensive endpoint coverage
|
|
95
105
|
- **Ruby Version Information**: Fetch Ruby release information, changelogs, and maintenance status from ruby-lang.org
|
|
96
|
-
- **MCP Server Integration**: Ready-to-use MCP server with
|
|
106
|
+
- **MCP Server Integration**: Ready-to-use MCP server with 18 tools and 4 resources, compatible with Cursor IDE, Claude Desktop, and other MCP-enabled tools
|
|
97
107
|
- **Pagination & Sorting**: Support for large result sets with customizable pagination and sorting options
|
|
98
108
|
- **Caching**: In-memory caching with configurable TTL for improved performance
|
|
99
109
|
- **Error Handling**: Graceful error handling with custom exceptions and response size limits
|
|
@@ -214,6 +224,8 @@ recently_updated = client.get_recently_updated_gems(limit: 10)
|
|
|
214
224
|
|
|
215
225
|
- `get_latest_gems(limit: 30)` - Get latest gems - most recently added gems to RubyGems.org
|
|
216
226
|
- `get_recently_updated_gems(limit: 30)` - Get recently updated gems - most recently updated gem versions
|
|
227
|
+
- `get_news_releases(page: 1)` - Get news releases - all new gem releases from RubyGems.org with pagination (fetches from `/news?page=N`)
|
|
228
|
+
- `get_popular_releases(page: 1)` - Get popular releases - popular new gem releases from RubyGems.org with pagination (fetches from `/releases/popular?page=N`)
|
|
217
229
|
|
|
218
230
|
## MCP Server Integration
|
|
219
231
|
|
|
@@ -274,7 +286,7 @@ The MCP server provides the following tools:
|
|
|
274
286
|
- Parameters: `gem_name` (string), `version` (optional string, uses latest if not provided)
|
|
275
287
|
|
|
276
288
|
13. **search_gems** - Search for gems by name on RubyGems
|
|
277
|
-
- Parameters: `query` (string)
|
|
289
|
+
- Parameters: `query` (string), `limit` (optional integer), `offset` (optional integer)
|
|
278
290
|
|
|
279
291
|
14. **get_ruby_roadmap** - Get Ruby roadmap information from bugs.ruby-lang.org showing planned versions and their issues
|
|
280
292
|
- Parameters: none
|
|
@@ -285,6 +297,14 @@ The MCP server provides the following tools:
|
|
|
285
297
|
16. **get_ruby_version_github_changelog** - Get GitHub release changelog for a Ruby version from the ruby/ruby repository
|
|
286
298
|
- Parameters: `version` (string, e.g., "3.4.7", "3.4.0")
|
|
287
299
|
|
|
300
|
+
17. **get_news_releases** - Get news releases - all new gem releases from RubyGems.org with pagination
|
|
301
|
+
- Parameters: `page` (optional integer, default: 1) - Page number (1-based)
|
|
302
|
+
- Fetches from: `https://rubygems.org/news?page=N`
|
|
303
|
+
|
|
304
|
+
18. **get_popular_releases** - Get popular releases - popular new gem releases from RubyGems.org with pagination
|
|
305
|
+
- Parameters: `page` (optional integer, default: 1) - Page number (1-based)
|
|
306
|
+
- Fetches from: `https://rubygems.org/releases/popular?page=N`
|
|
307
|
+
|
|
288
308
|
## MCP Resources
|
|
289
309
|
|
|
290
310
|
The MCP server provides the following resources:
|
|
@@ -307,10 +327,7 @@ The MCP server provides the following resources:
|
|
|
307
327
|
|
|
308
328
|
## Error Handling
|
|
309
329
|
|
|
310
|
-
|
|
311
|
-
- Returns empty arrays for failed requests
|
|
312
|
-
- Returns empty hashes for failed gem info requests
|
|
313
|
-
- Handles network errors and JSON parsing errors
|
|
330
|
+
HTTP failures raise `NotFoundError`, `ServerError`, or `APIError`. Unreadable JSON or HTML raises `CorruptedDataError`. Bodies over 5MB raise `ResponseSizeExceededError`. Invalid input, including more than 20 names on `get_latest_versions`, raises `ValidationError`.
|
|
314
331
|
|
|
315
332
|
## Development
|
|
316
333
|
|
|
@@ -352,6 +369,18 @@ For more information, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
|
352
369
|
|
|
353
370
|
If you discover a security vulnerability, please report it responsibly. See [SECURITY.md](SECURITY.md) for details.
|
|
354
371
|
|
|
372
|
+
## Links
|
|
373
|
+
|
|
374
|
+
- [GitHub](https://github.com/amkisko/rubygems_mcp.rb)
|
|
375
|
+
- [GitLab](https://gitlab.com/amkisko/rubygems_mcp.rb)
|
|
376
|
+
- [RubyGems](https://rubygems.org/gems/rubygems_mcp)
|
|
377
|
+
- [libraries.io](https://libraries.io/rubygems/rubygems_mcp)
|
|
378
|
+
- [Deps.dev](https://deps.dev/rubygems/rubygems_mcp)
|
|
379
|
+
- [SonarCloud](https://sonarcloud.io/project/overview?id=amkisko_rubygems_mcp.rb)
|
|
380
|
+
- [Snyk](https://snyk.io/test/github/amkisko/rubygems_mcp.rb)
|
|
381
|
+
- [Codecov](https://app.codecov.io/github/amkisko/rubygems_mcp.rb)
|
|
382
|
+
- [OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/amkisko/rubygems_mcp.rb)
|
|
383
|
+
|
|
355
384
|
## License
|
|
356
385
|
|
|
357
386
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubygemsMcp
|
|
4
|
+
class Cache
|
|
5
|
+
MAX_ENTRIES = 256
|
|
6
|
+
|
|
7
|
+
def initialize(max_entries: MAX_ENTRIES)
|
|
8
|
+
@cache = {}
|
|
9
|
+
@mutex = Mutex.new
|
|
10
|
+
@inflight = {}
|
|
11
|
+
@max_entries = max_entries
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(key)
|
|
15
|
+
@mutex.synchronize { read_unlocked(key) }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def set(key, value, ttl_seconds)
|
|
19
|
+
@mutex.synchronize { write_unlocked(key, value, ttl_seconds) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def fetch(key, ttl_seconds)
|
|
23
|
+
cached = get(key)
|
|
24
|
+
return cached unless cached.nil?
|
|
25
|
+
|
|
26
|
+
lock = @mutex.synchronize { @inflight[key] ||= Mutex.new }
|
|
27
|
+
lock.synchronize do
|
|
28
|
+
cached = get(key)
|
|
29
|
+
return cached unless cached.nil?
|
|
30
|
+
|
|
31
|
+
value = yield
|
|
32
|
+
set(key, value, ttl_seconds)
|
|
33
|
+
value
|
|
34
|
+
ensure
|
|
35
|
+
@mutex.synchronize { @inflight.delete(key) if @inflight[key].equal?(lock) }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def clear
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
@cache.clear
|
|
42
|
+
@inflight.clear
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def size
|
|
47
|
+
@mutex.synchronize { @cache.size }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def read_unlocked(key)
|
|
53
|
+
entry = @cache[key]
|
|
54
|
+
return nil unless entry
|
|
55
|
+
|
|
56
|
+
if entry[:expires_at] < Time.now
|
|
57
|
+
@cache.delete(key)
|
|
58
|
+
return nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
entry[:value]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def write_unlocked(key, value, ttl_seconds)
|
|
65
|
+
evict_unlocked if @cache.size >= @max_entries && !@cache.key?(key)
|
|
66
|
+
@cache.delete(key)
|
|
67
|
+
@cache[key] = {value: value, expires_at: Time.now + ttl_seconds}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def evict_unlocked
|
|
71
|
+
now = Time.now
|
|
72
|
+
@cache.delete_if { |_key, entry| entry[:expires_at] < now }
|
|
73
|
+
@cache.shift while @cache.size >= @max_entries
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|