rubygems_mcp 0.1.0 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: afa4ece822d5b8556b280d14c3755876420957ba7e6b080cb20aa529730bb93e
4
- data.tar.gz: 5e96e90380d30f47fe2e2f765cf4e5831093bfefde80757a60aede6cf46f5753
3
+ metadata.gz: a457f8426c2139f86ee038c95ceaca65c6b58ae60505f3f7c20e01f6d8df0b01
4
+ data.tar.gz: d8076f2259213d6a4737bbf4c3e1ec9f479354fc24a15bec3c3cdf7bd343bcbe
5
5
  SHA512:
6
- metadata.gz: 9f07be5cde29b994821781e0350711b02df4a4f22e93ace9b860dfa6e23d81354639c6d6078da12768ba50a32d95ea89a64224c355ae39efe5486acf9af432eb
7
- data.tar.gz: e648da457c4b257a2dabcf41e9b682825cb4d588012e8eefd0bd46c8b399578bb7ae9a50e87345228538efb2d7c5449904b4fc75f3206a28a229b1ebf9a80bbf
6
+ metadata.gz: 52e7b9d695199beeabd3becceaf587c30e2d5b8b862a6a5648c3d290ef9a5a365c3709f2d43547dac81d3366beef9d23a3d12d32ad7bbec4208bf90c12b89704
7
+ data.tar.gz: 5730b35728600a1b2f1526cd2d18d87167b62054910c4b3c026481743bd1bbaa36c4f05aa0722b51fc62439b40322d81aca96a57dc7687abff8a00beba4e67d8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.1.2 (2025-11-21)
4
+
5
+ - Enhance Ruby version changelog retrieval and increase maximum response size
6
+ - Rename rubygems key to rubygems-dev in mcp.json configuration for development environment setup
7
+
8
+ ## 0.1.1 (2025-11-21)
9
+
10
+ - Update fast-mcp dependency version constraints to allow versions >= 0.1 and < 2.0
11
+ - Fix Resource class references to use FastMcp::Resource instead of MCP::Resource
12
+ - Update resource methods from `default_content` to `content` to match fast-mcp 1.0.0+ API
13
+ - Fix test expectations to match fast-mcp API (tool names without `::` separators, resources as Array)
14
+ - Add explicit `tool_name` definitions to all tools for user-friendly snake_case names (e.g., `get_latest_versions` instead of long class names)
15
+ - Fix array argument definitions: Change from `filled(:array)` to `array(:string)` to fix JSON schema conversion errors with dry-schema 1.14.1+ (fixes "Could not find an equivalent conversion for type :array" error)
16
+
3
17
  ## 0.1.0 (2025-01-15)
4
18
 
5
19
  - Initial release
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # rubygems_mcp
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/rubygems_mcp.svg)](https://badge.fury.io/rb/rubygems_mcp) [![Test Status](https://github.com/amkisko/rubygems_mcp.rb/actions/workflows/test.yml/badge.svg)](https://github.com/amkisko/rubygems_mcp.rb/actions/workflows/test.yml)
3
+ [![Gem Version](https://badge.fury.io/rb/rubygems_mcp.svg?v=0.1.1)](https://badge.fury.io/rb/rubygems_mcp) [![Test Status](https://github.com/amkisko/rubygems_mcp.rb/actions/workflows/test.yml/badge.svg)](https://github.com/amkisko/rubygems_mcp.rb/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/amkisko/rubygems_mcp.rb/graph/badge.svg?token=APQ6AK7EC9)](https://codecov.io/gh/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
 
@@ -26,19 +26,6 @@ gem install rubygems_mcp
26
26
 
27
27
  For Cursor IDE, create or update `.cursor/mcp.json` in your project:
28
28
 
29
- ```json
30
- {
31
- "mcpServers": {
32
- "rubygems": {
33
- "command": "bundle",
34
- "args": ["exec", "rubygems_mcp"]
35
- }
36
- }
37
- }
38
- ```
39
-
40
- Or if installed globally:
41
-
42
29
  ```json
43
30
  {
44
31
  "mcpServers": {
@@ -56,20 +43,6 @@ For Claude Desktop, edit the MCP configuration file:
56
43
  **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
57
44
  **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
58
45
 
59
- ```json
60
- {
61
- "mcpServers": {
62
- "rubygems": {
63
- "command": "bundle",
64
- "args": ["exec", "rubygems_mcp"],
65
- "cwd": "/path/to/your/project"
66
- }
67
- }
68
- }
69
- ```
70
-
71
- Or if installed globally:
72
-
73
46
  ```json
74
47
  {
75
48
  "mcpServers": {
@@ -14,8 +14,8 @@ module RubygemsMcp
14
14
  # all_versions = client.get_gem_versions("rails")
15
15
  # ruby_version = client.get_latest_ruby_version
16
16
  class Client
17
- # Maximum response size (1MB) to protect against crawler protection pages
18
- MAX_RESPONSE_SIZE = 1024 * 1024 # 1MB
17
+ # Maximum response size (5MB) to protect against crawler protection pages
18
+ MAX_RESPONSE_SIZE = 5 * 1024 * 1024 # 5MB
19
19
 
20
20
  # Custom exception for corrupted data
21
21
  class CorruptedDataError < StandardError
@@ -349,18 +349,36 @@ module RubygemsMcp
349
349
  end
350
350
  end
351
351
 
352
- # Get changelog summary for a Ruby version from release notes
352
+ # Get full changelog content for a Ruby version from release notes
353
353
  #
354
- # @param version [String] Ruby version (e.g., "3.4.7")
355
- # @return [Hash] Hash with :version, :release_notes_url, and :summary
354
+ # @param version [String] Ruby version (e.g., "3.4.7" or "4.0.0-preview2")
355
+ # @return [Hash] Hash with :version, :release_notes_url, and :content (full content)
356
356
  def get_ruby_version_changelog(version)
357
357
  # First get the release notes URL for this version
358
358
  versions = get_ruby_versions
359
- version_data = versions.find { |v| v[:version] == version }
360
- return {version: version, release_notes_url: nil, summary: nil, error: "Version not found"} unless version_data
359
+
360
+ # Normalize the input version for comparison (handles formats like "4.0.0-preview2" -> "4.0.0.pre.preview2")
361
+ normalized_input = begin
362
+ Gem::Version.new(version).to_s
363
+ rescue ArgumentError
364
+ version
365
+ end
366
+
367
+ # Try exact match first, then normalized match
368
+ version_data = versions.find do |v|
369
+ v[:version] == version ||
370
+ v[:version] == normalized_input ||
371
+ begin
372
+ Gem::Version.new(v[:version]).to_s == normalized_input
373
+ rescue ArgumentError
374
+ false
375
+ end
376
+ end
377
+
378
+ return {version: version, release_notes_url: nil, content: nil, error: "Version not found"} unless version_data
361
379
 
362
380
  release_notes_url = version_data[:release_notes_url]
363
- return {version: version, release_notes_url: nil, summary: nil, error: "No release notes available"} unless release_notes_url
381
+ return {version: version, release_notes_url: nil, content: nil, error: "No release notes available"} unless release_notes_url
364
382
 
365
383
  cache_key = "ruby_changelog:#{version}"
366
384
 
@@ -371,29 +389,32 @@ module RubygemsMcp
371
389
 
372
390
  uri = URI(release_notes_url)
373
391
  response = make_request(uri, parse_html: true)
374
- return {version: version, release_notes_url: release_notes_url, summary: nil, error: "Failed to fetch release notes"} unless response
392
+ return {version: version, release_notes_url: release_notes_url, content: nil, error: "Failed to fetch release notes"} unless response
375
393
 
376
- # Extract the main content - typically in a div with class "content" or "entry-content"
377
- # Try multiple selectors to find the main content
378
- content = response.css("div.content, div.entry-content, article, main").first || response.css("body").first
394
+ # Extract the main content - Ruby release notes use div#content
395
+ content = response.css("div#content").first || response.css("div.content, div.entry-content, article, main").first
379
396
 
380
397
  if content
381
- # Extract text, remove excessive whitespace, and get first few paragraphs
398
+ # Remove navigation and metadata elements
399
+ content.css("p.post-info, .post-info, nav, .navigation, header, footer, .sidebar").remove
400
+
401
+ # Get the full text content, preserving structure
382
402
  text = content.text.strip
383
- # Split into paragraphs and take first 3-5 meaningful ones
384
- paragraphs = text.split(/\n\n+/).reject { |p| p.strip.length < 50 }
385
- summary = paragraphs.first(5).join("\n\n").strip
386
403
 
387
- # Limit summary length
388
- summary = summary[0..2000] + "..." if summary.length > 2000
404
+ # Clean up excessive whitespace but preserve paragraph structure
405
+ text = text.gsub(/\n{3,}/, "\n\n")
406
+ text = text.gsub(/[ \t]+/, " ")
407
+
408
+ # Remove empty lines at start/end
409
+ text = text.strip
389
410
  else
390
- summary = nil
411
+ text = nil
391
412
  end
392
413
 
393
414
  result = {
394
415
  version: version,
395
416
  release_notes_url: release_notes_url,
396
- summary: summary
417
+ content: text
397
418
  }
398
419
 
399
420
  # Cache for 24 hours
@@ -562,9 +583,10 @@ module RubygemsMcp
562
583
 
563
584
  # Extract the main content - try GitHub release page first, then generic selectors
564
585
  content = if changelog_uri.include?("github.com") && changelog_uri.include?("/releases/")
565
- # GitHub release page - look for release notes in markdown-body or release notes section
566
- response.css(".markdown-body, .release-body, [data-testid='release-body']").first ||
567
- response.css("div.repository-content, article").first
586
+ # GitHub release page - look for release notes in markdown-body
587
+ response.css(".markdown-body").first ||
588
+ response.css("[data-testid='release-body'], .release-body").first ||
589
+ response.css("div.repository-content article").first
568
590
  else
569
591
  # Generic changelog page
570
592
  response.css("div.content, div.entry-content, article, main, .markdown-body").first
@@ -573,29 +595,90 @@ module RubygemsMcp
573
595
  content ||= response.css("body").first
574
596
 
575
597
  summary = if content
598
+ # Remove UI elements, navigation, and error messages
599
+ content.css("nav, header, footer, .navigation, .sidebar, .blankslate, details, summary, .Box-footer, .Counter, [data-view-component], script, style").remove
600
+
601
+ # Remove elements with common UI classes
602
+ content.css("[class*='blankslate'], [class*='Box-footer'], [class*='Counter'], [class*='details-toggle']").remove
603
+
604
+ # Get text content
576
605
  text = content.text.strip
577
- # Remove common navigation/header text patterns
606
+
607
+ # Remove common GitHub UI text patterns
578
608
  text = text.gsub(/Notifications.*?signed in.*?reload/im, "")
579
609
  text = text.gsub(/You must be signed in.*?reload/im, "")
580
610
  text = text.gsub(/There was an error.*?reload/im, "")
611
+ text = text.gsub(/Please reload this page.*?/im, "")
612
+ text = text.gsub(/Loading.*?/im, "")
613
+ text = text.gsub(/Uh oh!.*?/im, "")
614
+ text = text.gsub(/Assets.*?\d+.*?/im, "")
615
+
616
+ # Remove commit hashes and issue references that are just links without context
617
+ text = text.gsub(/\b[a-f0-9]{7,40}\b/, "") # Remove commit hashes
618
+ text = text.gsub(/#\d+\s*$/, "") # Remove trailing issue numbers without context
619
+
620
+ # Clean up whitespace
621
+ text = text.gsub(/\n{3,}/, "\n\n")
622
+ text = text.gsub(/[ \t]{2,}/, " ")
623
+
624
+ # Split into lines and filter out irrelevant content
625
+ lines = text.split(/\n+/)
626
+
627
+ # Filter out lines that are likely UI elements or irrelevant
628
+ filtered_lines = []
629
+ prev_line_was_meaningful = false
630
+
631
+ lines.each_with_index do |line, idx|
632
+ stripped = line.strip
633
+ next if stripped.empty?
634
+
635
+ # Skip UI elements
636
+ next if stripped.match?(/^(Notifications|You must|There was|Please reload|Loading|Uh oh|Assets|\d+\s*$)/i)
637
+ next if stripped.match?(/^\/\s*$/)
638
+ next if stripped.match?(/^[a-f0-9]{7,40}$/) # Standalone commit hashes
639
+ next if stripped.match?(/^\s*#\d+\s*$/) # Standalone issue numbers
640
+
641
+ # Skip author names that appear alone (pattern: First Last or First Middle Last)
642
+ # Author names typically appear after a change description ends with punctuation
643
+ if stripped.match?(/^[A-Z][a-z]+(?:\s+[A-Z][a-z]+){1,2}$/) && stripped.length < 50 && !stripped.match?(/^[A-Z][a-z]+ [A-Z]\./) # Not initials like "J. Smith"
644
+ # Check if previous line ends with punctuation (end of sentence = author attribution follows)
645
+ if idx > 0 && filtered_lines.any?
646
+ prev = filtered_lines.last.to_s.strip
647
+ # If previous line ends with punctuation, this standalone name is likely an author
648
+ if prev.match?(/[.!]$/)
649
+ next
650
+ end
651
+ elsif idx == 0
652
+ # First line that's just a name, skip it
653
+ next
654
+ end
655
+ end
581
656
 
582
- # Split into paragraphs and take first 5-10 meaningful ones
583
- # Try splitting by double newlines first, then by single newlines if that doesn't work
584
- paragraphs = if text.include?("\n\n")
585
- text.split(/\n\n+/)
586
- else
587
- text.split(/\n+/)
657
+ # Keep meaningful lines
658
+ if stripped.length >= 10
659
+ filtered_lines << line
660
+ prev_line_was_meaningful = true
661
+ end
588
662
  end
589
663
 
590
- paragraphs = paragraphs.reject { |p|
591
- p.strip.length < 30 ||
592
- p.match?(/^(rails|Notifications|You must|There was)/i) ||
593
- p.match?(/^\/\s*$/)
594
- }
595
- summary_text = paragraphs.first(10).join("\n\n").strip
664
+ # Remove trailing "No changes." and similar repetitive endings
665
+ while filtered_lines.last&.strip&.match?(/^(No changes\.?|Guides)$/i)
666
+ filtered_lines.pop
667
+ end
668
+
669
+ # Join back and clean up
670
+ summary_text = filtered_lines.join("\n").strip
671
+
672
+ # Remove excessive blank lines
673
+ summary_text = summary_text.gsub(/\n{3,}/, "\n\n")
674
+
675
+ # Limit length but keep it reasonable for changelogs
676
+ if summary_text.length > 10000
677
+ # Try to cut at a reasonable point (end of a section)
678
+ cut_point = summary_text[0..10000].rindex(/\n\n/)
679
+ summary_text = summary_text[0..(cut_point || 10000)].strip + "\n\n..."
680
+ end
596
681
 
597
- # Limit summary length
598
- summary_text = summary_text[0..3000] + "..." if summary_text.length > 3000
599
682
  summary_text.empty? ? nil : summary_text
600
683
  end
601
684
 
@@ -16,6 +16,21 @@ FastMcp = MCP unless defined?(FastMcp)
16
16
  module MCP
17
17
  module Transports
18
18
  class StdioTransport
19
+ if method_defined?(:send_error)
20
+ alias_method :original_send_error, :send_error
21
+
22
+ def send_error(code, message, id = nil)
23
+ # Use placeholder id if nil to satisfy strict MCP client validation
24
+ # JSON-RPC 2.0 allows null for notifications, but MCP clients require valid id
25
+ id = "error_#{SecureRandom.hex(8)}" if id.nil?
26
+ original_send_error(code, message, id)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ class Server
33
+ if method_defined?(:send_error)
19
34
  alias_method :original_send_error, :send_error
20
35
 
21
36
  def send_error(code, message, id = nil)
@@ -26,17 +41,6 @@ module MCP
26
41
  end
27
42
  end
28
43
  end
29
-
30
- class Server
31
- alias_method :original_send_error, :send_error
32
-
33
- def send_error(code, message, id = nil)
34
- # Use placeholder id if nil to satisfy strict MCP client validation
35
- # JSON-RPC 2.0 allows null for notifications, but MCP clients require valid id
36
- id = "error_#{SecureRandom.hex(8)}" if id.nil?
37
- original_send_error(code, message, id)
38
- end
39
- end
40
44
  end
41
45
 
42
46
  module RubygemsMcp
@@ -151,11 +155,12 @@ module RubygemsMcp
151
155
 
152
156
  # Get latest versions for a list of gems with release dates
153
157
  class GetLatestVersionsTool < BaseTool
158
+ tool_name "get_latest_versions"
154
159
  description "Get latest versions for a list of gems with release dates and licenses. Supports GraphQL-like field selection."
155
160
 
156
161
  arguments do
157
- required(:gem_names).filled(:array, min_size?: 1).description("Array of gem names (e.g., ['rails', 'nokogiri', 'rack'])")
158
- optional(:fields).filled(:array).description("GraphQL-like field selection. Available: name, version, release_date, license, built_at, prerelease, platform, ruby_version, rubygems_version, downloads_count, sha, spec_sha, requirements, metadata")
162
+ required(:gem_names).array(:string, min_size?: 1).description("Array of gem names (e.g., ['rails', 'nokogiri', 'rack'])")
163
+ optional(:fields).array(:string).description("GraphQL-like field selection. Available: name, version, release_date, license, built_at, prerelease, platform, ruby_version, rubygems_version, downloads_count, sha, spec_sha, requirements, metadata")
159
164
  end
160
165
 
161
166
  def call(gem_names:, fields: nil)
@@ -165,6 +170,7 @@ module RubygemsMcp
165
170
 
166
171
  # Get all versions for a single gem
167
172
  class GetGemVersionsTool < BaseTool
173
+ tool_name "get_gem_versions"
168
174
  description "Get all versions for a single gem with release dates and licenses, sorted by version descending. Supports GraphQL-like field selection."
169
175
 
170
176
  arguments do
@@ -172,7 +178,7 @@ module RubygemsMcp
172
178
  optional(:limit).filled(:integer).description("Maximum number of versions to return (for pagination)")
173
179
  optional(:offset).filled(:integer).description("Number of versions to skip (for pagination)")
174
180
  optional(:sort).filled(:string).description("Sort order: version_desc, version_asc, date_desc, or date_asc (default: version_desc)")
175
- optional(:fields).filled(:array).description("GraphQL-like field selection. Available: version, release_date, license, built_at, prerelease, platform, ruby_version, rubygems_version, downloads_count, sha, spec_sha, requirements, metadata")
181
+ optional(:fields).array(:string).description("GraphQL-like field selection. Available: version, release_date, license, built_at, prerelease, platform, ruby_version, rubygems_version, downloads_count, sha, spec_sha, requirements, metadata")
176
182
  end
177
183
 
178
184
  def call(gem_name:, limit: nil, offset: 0, sort: "version_desc", fields: nil)
@@ -189,6 +195,7 @@ module RubygemsMcp
189
195
 
190
196
  # Get latest Ruby version with release date
191
197
  class GetLatestRubyVersionTool < BaseTool
198
+ tool_name "get_latest_ruby_version"
192
199
  description "Get latest Ruby version with release date"
193
200
 
194
201
  arguments do
@@ -202,6 +209,7 @@ module RubygemsMcp
202
209
 
203
210
  # Get all Ruby versions with release dates
204
211
  class GetRubyVersionsTool < BaseTool
212
+ tool_name "get_ruby_versions"
205
213
  description "Get all Ruby versions with release dates, download URLs, and release notes URLs, sorted by version descending"
206
214
 
207
215
  arguments do
@@ -224,6 +232,7 @@ module RubygemsMcp
224
232
 
225
233
  # Get changelog summary for a Ruby version
226
234
  class GetRubyVersionChangelogTool < BaseTool
235
+ tool_name "get_ruby_version_changelog"
227
236
  description "Get changelog summary for a specific Ruby version by fetching and parsing the release notes"
228
237
 
229
238
  arguments do
@@ -231,17 +240,41 @@ module RubygemsMcp
231
240
  end
232
241
 
233
242
  def call(version:)
234
- get_client.get_ruby_version_changelog(version)
243
+ result = get_client.get_ruby_version_changelog(version)
244
+ # Convert content to MCP-compliant format
245
+ # MCP expects content to be an array of content items with type and text fields
246
+ if result.is_a?(Hash)
247
+ if result[:content].is_a?(String) && !result[:content].empty?
248
+ result[:content] = [{type: "text", text: result[:content]}]
249
+ elsif result[:content].is_a?(String) && result[:content].empty?
250
+ result[:content] = []
251
+ elsif result[:content].nil?
252
+ result[:content] = []
253
+ elsif result[:content].is_a?(Array)
254
+ # Ensure array elements have the correct format
255
+ result[:content] = result[:content].map do |item|
256
+ if item.is_a?(String)
257
+ {type: "text", text: item}
258
+ elsif item.is_a?(Hash) && !item.key?(:type)
259
+ item.merge(type: "text")
260
+ else
261
+ item
262
+ end
263
+ end
264
+ end
265
+ end
266
+ result
235
267
  end
236
268
  end
237
269
 
238
270
  # Get gem information (summary, homepage, etc.)
239
271
  class GetGemInfoTool < BaseTool
272
+ tool_name "get_gem_info"
240
273
  description "Get detailed information about a gem (summary, homepage, source code, documentation, licenses, authors, dependencies, downloads). Supports GraphQL-like field selection."
241
274
 
242
275
  arguments do
243
276
  required(:gem_name).filled(:string).description("Gem name (e.g., 'rails')")
244
- optional(:fields).filled(:array).description("GraphQL-like field selection. Available: name, version, summary, description, homepage, source_code, documentation, licenses, authors, info, downloads, version_downloads, yanked, dependencies, changelog_uri, funding_uri, platform, sha, spec_sha, metadata")
277
+ optional(:fields).array(:string).description("GraphQL-like field selection. Available: name, version, summary, description, homepage, source_code, documentation, licenses, authors, info, downloads, version_downloads, yanked, dependencies, changelog_uri, funding_uri, platform, sha, spec_sha, metadata")
245
278
  end
246
279
 
247
280
  def call(gem_name:, fields: nil)
@@ -251,6 +284,7 @@ module RubygemsMcp
251
284
 
252
285
  # Get reverse dependencies (gems that depend on this gem)
253
286
  class GetGemReverseDependenciesTool < BaseTool
287
+ tool_name "get_gem_reverse_dependencies"
254
288
  description "Get reverse dependencies - list of gems that depend on the specified gem"
255
289
 
256
290
  arguments do
@@ -264,6 +298,7 @@ module RubygemsMcp
264
298
 
265
299
  # Get download statistics for a gem version
266
300
  class GetGemVersionDownloadsTool < BaseTool
301
+ tool_name "get_gem_version_downloads"
267
302
  description "Get download statistics for a specific gem version"
268
303
 
269
304
  arguments do
@@ -278,6 +313,7 @@ module RubygemsMcp
278
313
 
279
314
  # Get latest gems (most recently added)
280
315
  class GetLatestGemsTool < BaseTool
316
+ tool_name "get_latest_gems"
281
317
  description "Get latest gems - most recently added gems to RubyGems.org"
282
318
 
283
319
  arguments do
@@ -291,6 +327,7 @@ module RubygemsMcp
291
327
 
292
328
  # Get recently updated gems
293
329
  class GetRecentlyUpdatedGemsTool < BaseTool
330
+ tool_name "get_recently_updated_gems"
294
331
  description "Get recently updated gems - most recently updated gem versions"
295
332
 
296
333
  arguments do
@@ -304,6 +341,7 @@ module RubygemsMcp
304
341
 
305
342
  # Get changelog summary for a gem
306
343
  class GetGemChangelogTool < BaseTool
344
+ tool_name "get_gem_changelog"
307
345
  description "Get changelog summary for a gem by fetching and parsing the changelog from its changelog_uri"
308
346
 
309
347
  arguments do
@@ -318,6 +356,7 @@ module RubygemsMcp
318
356
 
319
357
  # Search for gems by name
320
358
  class SearchGemsTool < BaseTool
359
+ tool_name "search_gems"
321
360
  description "Search for gems by name on RubyGems"
322
361
 
323
362
  arguments do
@@ -330,13 +369,13 @@ module RubygemsMcp
330
369
  end
331
370
 
332
371
  # Resource: Popular Ruby gems list
333
- class PopularGemsResource < MCP::Resource
372
+ class PopularGemsResource < FastMcp::Resource
334
373
  uri "rubygems://popular"
335
374
  resource_name "Popular Ruby Gems"
336
375
  description "A curated list of popular Ruby gems with their latest versions"
337
376
  mime_type "application/json"
338
377
 
339
- def default_content
378
+ def content
340
379
  client = Client.new
341
380
  popular_gems = %w[
342
381
  rails nokogiri bundler rake rspec devise puma sidekiq
@@ -366,13 +405,13 @@ module RubygemsMcp
366
405
  end
367
406
 
368
407
  # Resource: Ruby version compatibility information
369
- class RubyVersionCompatibilityResource < MCP::Resource
408
+ class RubyVersionCompatibilityResource < FastMcp::Resource
370
409
  uri "rubygems://ruby/compatibility"
371
410
  resource_name "Ruby Version Compatibility"
372
411
  description "Information about Ruby version compatibility and release dates"
373
412
  mime_type "application/json"
374
413
 
375
- def default_content
414
+ def content
376
415
  client = Client.new
377
416
  ruby_versions = client.get_ruby_versions(limit: 20, sort: :version_desc)
378
417
  latest = client.get_latest_ruby_version
@@ -402,13 +441,13 @@ module RubygemsMcp
402
441
  end
403
442
 
404
443
  # Resource: Ruby maintenance status for all versions
405
- class RubyMaintenanceStatusResource < MCP::Resource
444
+ class RubyMaintenanceStatusResource < FastMcp::Resource
406
445
  uri "rubygems://ruby/maintenance"
407
446
  resource_name "Ruby Maintenance Status"
408
447
  description "Detailed maintenance status for all Ruby versions including EOL dates and maintenance phases"
409
448
  mime_type "application/json"
410
449
 
411
- def default_content
450
+ def content
412
451
  client = Client.new
413
452
  maintenance_status = client.get_ruby_maintenance_status
414
453
 
@@ -428,13 +467,13 @@ module RubygemsMcp
428
467
  end
429
468
 
430
469
  # Resource: Latest Ruby version
431
- class LatestRubyVersionResource < MCP::Resource
470
+ class LatestRubyVersionResource < FastMcp::Resource
432
471
  uri "rubygems://ruby/latest"
433
472
  resource_name "Latest Ruby Version"
434
473
  description "The latest stable Ruby version with release date"
435
474
  mime_type "application/json"
436
475
 
437
- def default_content
476
+ def content
438
477
  client = Client.new
439
478
  latest = client.get_latest_ruby_version
440
479
  JSON.pretty_generate(latest)
@@ -1,3 +1,3 @@
1
1
  module RubygemsMcp
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.2"
3
3
  end
data/sig/rubygems_mcp.rbs CHANGED
@@ -59,7 +59,7 @@ module RubygemsMcp
59
59
  ) -> Array[Hash[Symbol, (String | nil)]]
60
60
 
61
61
  # Get changelog for a Ruby version
62
- def get_ruby_version_changelog: (String version) -> Hash[Symbol, (String | nil)]
62
+ def get_ruby_version_changelog: (String version) -> Hash[Symbol, (String | nil)] # Returns :version, :release_notes_url, :content
63
63
 
64
64
  # Get reverse dependencies (gems that depend on this gem)
65
65
  def get_gem_reverse_dependencies: (String gem_name) -> Array[String]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubygems_mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Makarov
@@ -13,16 +13,22 @@ dependencies:
13
13
  name: fast-mcp
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: '0.1'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
19
22
  type: :runtime
20
23
  prerelease: false
21
24
  version_requirements: !ruby/object:Gem::Requirement
22
25
  requirements:
23
- - - "~>"
26
+ - - ">="
24
27
  - !ruby/object:Gem::Version
25
28
  version: '0.1'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '2.0'
26
32
  - !ruby/object:Gem::Dependency
27
33
  name: nokogiri
28
34
  requirement: !ruby/object:Gem::Requirement