cf-mcp 0.16.2 → 0.17.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 +17 -0
- data/Manifest.txt +1 -0
- data/lib/cf/mcp/cli.rb +0 -2
- data/lib/cf/mcp/downloader.rb +53 -11
- data/lib/cf/mcp/github_client.rb +42 -0
- data/lib/cf/mcp/index_builder.rb +0 -5
- data/lib/cf/mcp/models/enum_doc.rb +0 -2
- data/lib/cf/mcp/models/function_doc.rb +0 -2
- data/lib/cf/mcp/models/struct_doc.rb +0 -2
- data/lib/cf/mcp/parser.rb +0 -4
- data/lib/cf/mcp/topic_parser.rb +0 -2
- data/lib/cf/mcp/version.rb +1 -1
- data/lib/cf/mcp.rb +17 -10
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d62d42b607e127916235fb3bbd2a2dace73fc1aefc9216dc1d9f92acfa6c423a
|
|
4
|
+
data.tar.gz: 12a75fda9901e78aee0fc6f7c3ed448ce52f6130d868f036aa674464efea640c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 63eabad61e8ea796f628e8f6248ac3e147b8486ac7495ab897dbd596d13044bd6495032dabede788a25a9426fcb9b855e5ced42a541b47aaeb269e161d958887
|
|
7
|
+
data.tar.gz: a262522cae34cbcb9b2f9c54252709b86fa44efbf2f32f8426937784cc35d5aaf048c9267ca4e9ee53bf1b3543f5df0e4d8ca76580a881cc94b0d4ab44f355a5
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.17.0] - 2026-02-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- SHA-based caching for downloaded Cute Framework headers - checks GitHub API for latest commit SHA before downloading to avoid redundant fetches
|
|
15
|
+
- GitHub API client with optional GITHUB_TOKEN support for higher rate limits (5000/hr vs 60/hr unauthenticated)
|
|
16
|
+
- Metadata tracking (.cf-mcp-sha file) stores downloaded version SHA for cache validation
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Downloader uses commit-specific archive URLs (e.g., /archive/abc1234.zip) instead of always downloading master branch
|
|
21
|
+
- Download process now checks for updates by comparing stored SHA with latest GitHub commit
|
|
22
|
+
- Refactored all internal `require_relative` statements to use `autoload` for lazy loading, improving load time and memory usage
|
|
23
|
+
|
|
8
24
|
## [0.16.2] - 2026-01-31
|
|
9
25
|
|
|
10
26
|
### Fixed
|
|
@@ -261,6 +277,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
261
277
|
- `cf_list_category` - List items by category
|
|
262
278
|
- `cf_get_details` - Get full documentation by name
|
|
263
279
|
|
|
280
|
+
[0.17.0]: https://github.com/pusewicz/cf-mcp/compare/v0.16.2...v0.17.0
|
|
264
281
|
[0.16.2]: https://github.com/pusewicz/cf-mcp/compare/v0.16.1...v0.16.2
|
|
265
282
|
[0.16.1]: https://github.com/pusewicz/cf-mcp/compare/v0.16.0...v0.16.1
|
|
266
283
|
[0.16.0]: https://github.com/pusewicz/cf-mcp/compare/v0.15.5...v0.16.0
|
data/Manifest.txt
CHANGED
data/lib/cf/mcp/cli.rb
CHANGED
data/lib/cf/mcp/downloader.rb
CHANGED
|
@@ -4,12 +4,13 @@ require "net/http"
|
|
|
4
4
|
require "uri"
|
|
5
5
|
require "fileutils"
|
|
6
6
|
require "zip"
|
|
7
|
-
require_relative "version"
|
|
8
7
|
|
|
9
8
|
module CF
|
|
10
9
|
module MCP
|
|
11
10
|
class Downloader
|
|
12
11
|
CUTE_FRAMEWORK_ZIP_URL = "https://github.com/RandyGaul/cute_framework/archive/refs/heads/master.zip"
|
|
12
|
+
GITHUB_ARCHIVE_URL_TEMPLATE = "https://github.com/RandyGaul/cute_framework/archive/%{ref}.zip"
|
|
13
|
+
SHA_METADATA_FILE = ".cf-mcp-sha"
|
|
13
14
|
DEFAULT_DOWNLOAD_DIR = File.join(Dir.tmpdir, "cf-mcp-#{VERSION}")
|
|
14
15
|
|
|
15
16
|
class DownloadError < StandardError; end
|
|
@@ -24,23 +25,41 @@ module CF
|
|
|
24
25
|
zip_path = File.join(@download_dir, "cute_framework.zip")
|
|
25
26
|
base_path = File.join(@download_dir, "cute_framework")
|
|
26
27
|
include_path = File.join(base_path, "include")
|
|
27
|
-
File.join(
|
|
28
|
+
sha_file = File.join(@download_dir, SHA_METADATA_FILE)
|
|
28
29
|
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
# Check if cache is valid
|
|
31
|
+
stored_sha = read_sha_metadata(sha_file)
|
|
32
|
+
latest_sha = fetch_latest_sha
|
|
33
|
+
|
|
34
|
+
if stored_sha && latest_sha && stored_sha == latest_sha
|
|
35
|
+
if File.directory?(include_path) && !Dir.empty?(include_path)
|
|
36
|
+
warn "Using cached Cute Framework headers (SHA: #{stored_sha})"
|
|
37
|
+
return include_path
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Determine download URL
|
|
42
|
+
if latest_sha
|
|
43
|
+
download_url = format(GITHUB_ARCHIVE_URL_TEMPLATE, ref: latest_sha)
|
|
44
|
+
warn "Downloading Cute Framework at SHA #{latest_sha}..."
|
|
45
|
+
else
|
|
46
|
+
download_url = CUTE_FRAMEWORK_ZIP_URL
|
|
47
|
+
warn "Downloading Cute Framework from master branch..."
|
|
32
48
|
end
|
|
33
49
|
|
|
34
|
-
download_zip(zip_path)
|
|
50
|
+
download_zip(zip_path, download_url)
|
|
35
51
|
extract_directories(zip_path, base_path)
|
|
36
52
|
|
|
53
|
+
# Store metadata for future cache checks
|
|
54
|
+
write_sha_metadata(sha_file, latest_sha) if latest_sha
|
|
55
|
+
|
|
37
56
|
include_path
|
|
38
57
|
end
|
|
39
58
|
|
|
40
59
|
private
|
|
41
60
|
|
|
42
|
-
def download_zip(destination)
|
|
43
|
-
uri = URI.parse(
|
|
61
|
+
def download_zip(destination, url = CUTE_FRAMEWORK_ZIP_URL)
|
|
62
|
+
uri = URI.parse(url)
|
|
44
63
|
|
|
45
64
|
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
|
|
46
65
|
request = Net::HTTP::Get.new(uri)
|
|
@@ -73,9 +92,9 @@ module CF
|
|
|
73
92
|
top_level_prefix = nil
|
|
74
93
|
|
|
75
94
|
zip_file.each do |entry|
|
|
76
|
-
# Find the top-level directory prefix (e.g., "cute_framework-master/")
|
|
77
|
-
if top_level_prefix.nil? && entry.name.match?(%r{^[^/]+/include/})
|
|
78
|
-
top_level_prefix = entry.name.match(%r{^([^/]+/)})[1]
|
|
95
|
+
# Find the top-level directory prefix (e.g., "cute_framework-master/" or "cute_framework-abc1234/")
|
|
96
|
+
if top_level_prefix.nil? && entry.name.match?(%r{^cute_framework-[^/]+/include/})
|
|
97
|
+
top_level_prefix = entry.name.match(%r{^(cute_framework-[^/]+/)})[1]
|
|
79
98
|
break
|
|
80
99
|
end
|
|
81
100
|
end
|
|
@@ -107,6 +126,29 @@ module CF
|
|
|
107
126
|
end
|
|
108
127
|
end
|
|
109
128
|
end
|
|
129
|
+
|
|
130
|
+
def fetch_latest_sha
|
|
131
|
+
client = GitHubClient.new
|
|
132
|
+
client.latest_commit_sha
|
|
133
|
+
rescue => e
|
|
134
|
+
warn "GitHub API error: #{e.message}"
|
|
135
|
+
nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def read_sha_metadata(file)
|
|
139
|
+
return nil unless File.exist?(file)
|
|
140
|
+
File.read(file).strip
|
|
141
|
+
rescue => e
|
|
142
|
+
warn "Error reading SHA metadata: #{e.message}"
|
|
143
|
+
nil
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def write_sha_metadata(file, sha)
|
|
147
|
+
return unless sha
|
|
148
|
+
File.write(file, sha)
|
|
149
|
+
rescue => e
|
|
150
|
+
warn "Error writing SHA metadata: #{e.message}"
|
|
151
|
+
end
|
|
110
152
|
end
|
|
111
153
|
end
|
|
112
154
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
module CF
|
|
8
|
+
module MCP
|
|
9
|
+
class GitHubClient
|
|
10
|
+
GITHUB_API_BASE = "https://api.github.com"
|
|
11
|
+
REPO_OWNER = "RandyGaul"
|
|
12
|
+
REPO_NAME = "cute_framework"
|
|
13
|
+
DEFAULT_BRANCH = "master"
|
|
14
|
+
|
|
15
|
+
def initialize(token: ENV["GITHUB_TOKEN"])
|
|
16
|
+
@token = token
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Returns latest commit SHA (short 7-char format) or nil on failure
|
|
20
|
+
def latest_commit_sha
|
|
21
|
+
uri = URI.parse("#{GITHUB_API_BASE}/repos/#{REPO_OWNER}/#{REPO_NAME}/commits/#{DEFAULT_BRANCH}")
|
|
22
|
+
|
|
23
|
+
request = Net::HTTP::Get.new(uri)
|
|
24
|
+
request["Accept"] = "application/vnd.github+json"
|
|
25
|
+
request["Authorization"] = "Bearer #{@token}" if @token
|
|
26
|
+
request["User-Agent"] = "cf-mcp/#{CF::MCP::VERSION}"
|
|
27
|
+
|
|
28
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
|
29
|
+
http.request(request)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
33
|
+
|
|
34
|
+
data = JSON.parse(response.body)
|
|
35
|
+
data.dig("sha")&.slice(0, 7) # Return short SHA
|
|
36
|
+
rescue
|
|
37
|
+
# Return nil on any error (network, JSON parse, etc.)
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/cf/mcp/index_builder.rb
CHANGED
data/lib/cf/mcp/parser.rb
CHANGED
data/lib/cf/mcp/topic_parser.rb
CHANGED
data/lib/cf/mcp/version.rb
CHANGED
data/lib/cf/mcp.rb
CHANGED
|
@@ -2,25 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
require "pathname"
|
|
4
4
|
require_relative "mcp/version"
|
|
5
|
-
require_relative "mcp/models/doc_item"
|
|
6
|
-
require_relative "mcp/models/function_doc"
|
|
7
|
-
require_relative "mcp/models/struct_doc"
|
|
8
|
-
require_relative "mcp/models/enum_doc"
|
|
9
|
-
require_relative "mcp/parser"
|
|
10
|
-
require_relative "mcp/index"
|
|
11
|
-
require_relative "mcp/index_builder"
|
|
12
|
-
require_relative "mcp/server"
|
|
13
|
-
require_relative "mcp/downloader"
|
|
14
|
-
require_relative "mcp/cli"
|
|
15
5
|
|
|
16
6
|
module CF
|
|
17
7
|
module MCP
|
|
18
8
|
class Error < StandardError; end
|
|
19
9
|
|
|
10
|
+
autoload :Parser, "cf/mcp/parser"
|
|
11
|
+
autoload :Index, "cf/mcp/index"
|
|
12
|
+
autoload :IndexBuilder, "cf/mcp/index_builder"
|
|
13
|
+
autoload :TopicParser, "cf/mcp/topic_parser"
|
|
14
|
+
autoload :Server, "cf/mcp/server"
|
|
15
|
+
autoload :Downloader, "cf/mcp/downloader"
|
|
16
|
+
autoload :GitHubClient, "cf/mcp/github_client"
|
|
17
|
+
autoload :CLI, "cf/mcp/cli"
|
|
18
|
+
|
|
20
19
|
def self.root
|
|
21
20
|
@root ||= Pathname.new(File.expand_path("../..", __dir__))
|
|
22
21
|
end
|
|
23
22
|
|
|
23
|
+
module Models
|
|
24
|
+
autoload :DocItem, "cf/mcp/models/doc_item"
|
|
25
|
+
autoload :FunctionDoc, "cf/mcp/models/function_doc"
|
|
26
|
+
autoload :StructDoc, "cf/mcp/models/struct_doc"
|
|
27
|
+
autoload :EnumDoc, "cf/mcp/models/enum_doc"
|
|
28
|
+
autoload :TopicDoc, "cf/mcp/models/topic_doc"
|
|
29
|
+
end
|
|
30
|
+
|
|
24
31
|
module Tools
|
|
25
32
|
autoload :SearchTool, "cf/mcp/tools/search_tool"
|
|
26
33
|
autoload :ListCategory, "cf/mcp/tools/list_category"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cf-mcp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Piotr Usewicz
|
|
@@ -98,6 +98,7 @@ files:
|
|
|
98
98
|
- lib/cf/mcp.rb
|
|
99
99
|
- lib/cf/mcp/cli.rb
|
|
100
100
|
- lib/cf/mcp/downloader.rb
|
|
101
|
+
- lib/cf/mcp/github_client.rb
|
|
101
102
|
- lib/cf/mcp/index.rb
|
|
102
103
|
- lib/cf/mcp/index_builder.rb
|
|
103
104
|
- lib/cf/mcp/models/doc_item.rb
|
|
@@ -152,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
152
153
|
- !ruby/object:Gem::Version
|
|
153
154
|
version: '0'
|
|
154
155
|
requirements: []
|
|
155
|
-
rubygems_version: 4.0.
|
|
156
|
+
rubygems_version: 4.0.6
|
|
156
157
|
specification_version: 4
|
|
157
158
|
summary: MCP server providing documentation tools for Cute Framework
|
|
158
159
|
test_files: []
|