cf-mcp 0.9.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 +7 -0
- data/LICENSE.txt +21 -0
- data/Manifest.txt +34 -0
- data/README.md +52 -0
- data/Rakefile +53 -0
- data/config.ru +26 -0
- data/exe/cf-mcp +6 -0
- data/lib/cf/mcp/cli.rb +211 -0
- data/lib/cf/mcp/downloader.rb +112 -0
- data/lib/cf/mcp/index.rb +128 -0
- data/lib/cf/mcp/models/doc_item.rb +171 -0
- data/lib/cf/mcp/models/enum_doc.rb +83 -0
- data/lib/cf/mcp/models/function_doc.rb +110 -0
- data/lib/cf/mcp/models/struct_doc.rb +83 -0
- data/lib/cf/mcp/models/topic_doc.rb +113 -0
- data/lib/cf/mcp/parser.rb +246 -0
- data/lib/cf/mcp/server.rb +316 -0
- data/lib/cf/mcp/templates/index.erb +94 -0
- data/lib/cf/mcp/templates/script.js +292 -0
- data/lib/cf/mcp/templates/style.css +165 -0
- data/lib/cf/mcp/tools/find_related.rb +77 -0
- data/lib/cf/mcp/tools/get_details.rb +64 -0
- data/lib/cf/mcp/tools/get_topic.rb +53 -0
- data/lib/cf/mcp/tools/list_category.rb +77 -0
- data/lib/cf/mcp/tools/list_topics.rb +64 -0
- data/lib/cf/mcp/tools/member_search.rb +76 -0
- data/lib/cf/mcp/tools/parameter_search.rb +102 -0
- data/lib/cf/mcp/tools/search_enums.rb +57 -0
- data/lib/cf/mcp/tools/search_functions.rb +57 -0
- data/lib/cf/mcp/tools/search_structs.rb +57 -0
- data/lib/cf/mcp/tools/search_tool.rb +58 -0
- data/lib/cf/mcp/topic_parser.rb +199 -0
- data/lib/cf/mcp/version.rb +7 -0
- data/lib/cf/mcp.rb +23 -0
- data/sig/cf/mcp.rbs +84 -0
- metadata +150 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "models/topic_doc"
|
|
4
|
+
|
|
5
|
+
module CF
|
|
6
|
+
module MCP
|
|
7
|
+
class TopicParser
|
|
8
|
+
# Pattern to match markdown links: [text](../category/name.md)
|
|
9
|
+
API_LINK_PATTERN = %r{\[`?([^\]]+)`?\]\(\.\./(\w+)/(\w+)\.md\)}
|
|
10
|
+
|
|
11
|
+
# Pattern to match topic links: [text](./topic_name.md) or [text](../topics/topic_name.md)
|
|
12
|
+
TOPIC_LINK_PATTERN = %r{\[([^\]]+)\]\((?:\./|\.\./topics/)(\w+)\.md\)}
|
|
13
|
+
|
|
14
|
+
# Pattern to match section headings
|
|
15
|
+
SECTION_PATTERN = /^##\s+(.+)$/
|
|
16
|
+
|
|
17
|
+
def parse_file(path)
|
|
18
|
+
content = File.read(path)
|
|
19
|
+
filename = File.basename(path, ".md")
|
|
20
|
+
|
|
21
|
+
return nil if filename == "index"
|
|
22
|
+
|
|
23
|
+
parse_topic(content, filename, File.basename(path))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def parse_directory(path)
|
|
27
|
+
topics = []
|
|
28
|
+
reading_order = parse_reading_order(File.join(path, "index.md"))
|
|
29
|
+
|
|
30
|
+
Dir.glob(File.join(path, "*.md")).each do |topic_file|
|
|
31
|
+
next if File.basename(topic_file) == "index.md"
|
|
32
|
+
|
|
33
|
+
topic = parse_file(topic_file)
|
|
34
|
+
if topic
|
|
35
|
+
topic.reading_order = reading_order[topic.name]
|
|
36
|
+
topics << topic
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
topics
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def parse_reading_order(index_path)
|
|
44
|
+
return {} unless File.exist?(index_path)
|
|
45
|
+
|
|
46
|
+
content = File.read(index_path)
|
|
47
|
+
order = {}
|
|
48
|
+
position = 0
|
|
49
|
+
|
|
50
|
+
# Match numbered list items with topic links
|
|
51
|
+
content.scan(/^\d+\.\s+\[([^\]]+)\]\(\.\/(\w+)\.md\)/) do |_title, slug|
|
|
52
|
+
order[slug] = position
|
|
53
|
+
position += 1
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
order
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def parse_topic(content, slug, source_file)
|
|
62
|
+
extract_title(content)
|
|
63
|
+
brief = extract_brief(content)
|
|
64
|
+
sections = extract_sections(content)
|
|
65
|
+
|
|
66
|
+
func_refs, struct_refs, enum_refs = extract_api_references(content)
|
|
67
|
+
topic_refs = extract_topic_references(content)
|
|
68
|
+
|
|
69
|
+
category = derive_category(slug)
|
|
70
|
+
|
|
71
|
+
Models::TopicDoc.new(
|
|
72
|
+
name: slug,
|
|
73
|
+
brief: brief,
|
|
74
|
+
category: category,
|
|
75
|
+
content: content,
|
|
76
|
+
sections: sections,
|
|
77
|
+
function_references: func_refs.uniq,
|
|
78
|
+
struct_references: struct_refs.uniq,
|
|
79
|
+
enum_references: enum_refs.uniq,
|
|
80
|
+
topic_references: topic_refs.uniq,
|
|
81
|
+
source_file: source_file
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def extract_title(content)
|
|
86
|
+
content.lines.find { |line| line.start_with?("# ") }&.sub(/^#\s+/, "")&.strip
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def extract_brief(content)
|
|
90
|
+
lines = content.lines
|
|
91
|
+
in_paragraph = false
|
|
92
|
+
paragraph_lines = []
|
|
93
|
+
|
|
94
|
+
lines.each do |line|
|
|
95
|
+
next if line.start_with?("#")
|
|
96
|
+
|
|
97
|
+
if line.strip.empty?
|
|
98
|
+
break if in_paragraph
|
|
99
|
+
next
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
in_paragraph = true
|
|
103
|
+
paragraph_lines << line.strip
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Strip markdown links but keep the text
|
|
107
|
+
paragraph_lines.join(" ").gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def extract_sections(content)
|
|
111
|
+
sections = []
|
|
112
|
+
current_title = nil
|
|
113
|
+
current_content = []
|
|
114
|
+
|
|
115
|
+
content.lines.each do |line|
|
|
116
|
+
if line =~ SECTION_PATTERN
|
|
117
|
+
if current_title
|
|
118
|
+
sections << Models::TopicDoc::Section.new(
|
|
119
|
+
title: current_title,
|
|
120
|
+
content: current_content.join
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
current_title = ::Regexp.last_match(1).strip
|
|
124
|
+
current_content = []
|
|
125
|
+
elsif current_title
|
|
126
|
+
current_content << line
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Add last section
|
|
131
|
+
if current_title
|
|
132
|
+
sections << Models::TopicDoc::Section.new(
|
|
133
|
+
title: current_title,
|
|
134
|
+
content: current_content.join
|
|
135
|
+
)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
sections
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def extract_api_references(content)
|
|
142
|
+
func_refs = []
|
|
143
|
+
struct_refs = []
|
|
144
|
+
enum_refs = []
|
|
145
|
+
|
|
146
|
+
content.scan(API_LINK_PATTERN) do |_text, _category, name|
|
|
147
|
+
if name.start_with?("cf_")
|
|
148
|
+
func_refs << name
|
|
149
|
+
elsif name.start_with?("CF_") || name.match?(/^[A-Z]/)
|
|
150
|
+
# Uppercase names are likely structs or enums
|
|
151
|
+
# Will be refined when cross-referenced with index
|
|
152
|
+
struct_refs << name
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
[func_refs, struct_refs, enum_refs]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def extract_topic_references(content)
|
|
160
|
+
refs = []
|
|
161
|
+
content.scan(TOPIC_LINK_PATTERN) do |_text, slug|
|
|
162
|
+
refs << slug
|
|
163
|
+
end
|
|
164
|
+
refs
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def derive_category(slug)
|
|
168
|
+
CATEGORY_MAP[slug] || slug
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
CATEGORY_MAP = {
|
|
172
|
+
"audio" => "audio",
|
|
173
|
+
"camera" => "draw",
|
|
174
|
+
"collision" => "collision",
|
|
175
|
+
"coroutines" => "coroutine",
|
|
176
|
+
"drawing" => "draw",
|
|
177
|
+
"input" => "input",
|
|
178
|
+
"networking" => "net",
|
|
179
|
+
"strings" => "string",
|
|
180
|
+
"random_numbers" => "math",
|
|
181
|
+
"application_window" => "app",
|
|
182
|
+
"game_loop_and_time" => "time",
|
|
183
|
+
"file_io" => "file",
|
|
184
|
+
"virtual_file_system" => "file",
|
|
185
|
+
"multithreading" => "thread",
|
|
186
|
+
"atomics" => "atomic",
|
|
187
|
+
"data_structures" => "array",
|
|
188
|
+
"allocator" => "alloc",
|
|
189
|
+
"emscripten" => "app",
|
|
190
|
+
"ios" => "app",
|
|
191
|
+
"web" => "https",
|
|
192
|
+
"dear_imgui" => "imgui",
|
|
193
|
+
"low_level_graphics" => "graphics",
|
|
194
|
+
"renderer" => "graphics",
|
|
195
|
+
"shader_compilation" => "graphics"
|
|
196
|
+
}.freeze
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
data/lib/cf/mcp.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
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/server"
|
|
12
|
+
require_relative "mcp/downloader"
|
|
13
|
+
require_relative "mcp/cli"
|
|
14
|
+
|
|
15
|
+
module CF
|
|
16
|
+
module MCP
|
|
17
|
+
class Error < StandardError; end
|
|
18
|
+
|
|
19
|
+
def self.root
|
|
20
|
+
@root ||= Pathname.new(File.expand_path("../..", __dir__))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/sig/cf/mcp.rbs
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module CF
|
|
2
|
+
module MCP
|
|
3
|
+
VERSION: String
|
|
4
|
+
|
|
5
|
+
class Error < StandardError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class Parser
|
|
9
|
+
def parse_file: (String path) -> Array[Models::DocItem]
|
|
10
|
+
def parse_directory: (String path) -> Array[Models::DocItem]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Index
|
|
14
|
+
attr_reader items: Hash[String, Models::DocItem]
|
|
15
|
+
attr_reader by_type: Hash[Symbol, Array[Models::DocItem]]
|
|
16
|
+
attr_reader by_category: Hash[String, Array[Models::DocItem]]
|
|
17
|
+
|
|
18
|
+
def add: (Models::DocItem item) -> void
|
|
19
|
+
def find: (String name) -> Models::DocItem?
|
|
20
|
+
def search: (String query, ?type: Symbol?, ?category: String?, ?limit: Integer) -> Array[Models::DocItem]
|
|
21
|
+
def functions: () -> Array[Models::DocItem]
|
|
22
|
+
def structs: () -> Array[Models::DocItem]
|
|
23
|
+
def enums: () -> Array[Models::DocItem]
|
|
24
|
+
def categories: () -> Array[String]
|
|
25
|
+
def items_in_category: (String category) -> Array[Models::DocItem]
|
|
26
|
+
def size: () -> Integer
|
|
27
|
+
def stats: () -> Hash[Symbol, Integer]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class CLI
|
|
31
|
+
def initialize: (Array[String] args) -> void
|
|
32
|
+
def run: () -> void
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class Server
|
|
36
|
+
attr_reader server: untyped
|
|
37
|
+
attr_reader index: Index
|
|
38
|
+
|
|
39
|
+
def initialize: (Index index) -> void
|
|
40
|
+
def run_stdio: () -> void
|
|
41
|
+
def run_http: (?port: Integer) -> void
|
|
42
|
+
def run_sse: (?port: Integer) -> void
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module Models
|
|
46
|
+
class DocItem
|
|
47
|
+
attr_accessor name: String?
|
|
48
|
+
attr_accessor type: Symbol?
|
|
49
|
+
attr_accessor category: String?
|
|
50
|
+
attr_accessor brief: String?
|
|
51
|
+
attr_accessor remarks: String?
|
|
52
|
+
attr_accessor example: String?
|
|
53
|
+
attr_accessor example_brief: String?
|
|
54
|
+
attr_accessor related: Array[String]
|
|
55
|
+
attr_accessor source_file: String?
|
|
56
|
+
|
|
57
|
+
def matches?: (String query) -> bool
|
|
58
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
59
|
+
def to_summary: () -> String
|
|
60
|
+
def to_text: (?detailed: bool) -> String
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class FunctionDoc < DocItem
|
|
64
|
+
attr_accessor signature: String?
|
|
65
|
+
attr_accessor parameters: Array[Parameter]
|
|
66
|
+
attr_accessor return_value: String?
|
|
67
|
+
|
|
68
|
+
Parameter: untyped
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
class StructDoc < DocItem
|
|
72
|
+
attr_accessor members: Array[Member]
|
|
73
|
+
|
|
74
|
+
Member: untyped
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class EnumDoc < DocItem
|
|
78
|
+
attr_accessor entries: Array[Entry]
|
|
79
|
+
|
|
80
|
+
Entry: untyped
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cf-mcp
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.9.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Piotr Usewicz
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: mcp
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.5'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.5'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: puma
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '6.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '6.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rack
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rackup
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '2.0'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '2.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rubyzip
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '2.3'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '2.3'
|
|
82
|
+
description: An MCP server that indexes Cute Framework header files and provides search
|
|
83
|
+
functionality for structs, functions, enums, and other elements.
|
|
84
|
+
email:
|
|
85
|
+
- piotr@layer22.com
|
|
86
|
+
executables:
|
|
87
|
+
- cf-mcp
|
|
88
|
+
extensions: []
|
|
89
|
+
extra_rdoc_files: []
|
|
90
|
+
files:
|
|
91
|
+
- LICENSE.txt
|
|
92
|
+
- Manifest.txt
|
|
93
|
+
- README.md
|
|
94
|
+
- Rakefile
|
|
95
|
+
- config.ru
|
|
96
|
+
- exe/cf-mcp
|
|
97
|
+
- lib/cf/mcp.rb
|
|
98
|
+
- lib/cf/mcp/cli.rb
|
|
99
|
+
- lib/cf/mcp/downloader.rb
|
|
100
|
+
- lib/cf/mcp/index.rb
|
|
101
|
+
- lib/cf/mcp/models/doc_item.rb
|
|
102
|
+
- lib/cf/mcp/models/enum_doc.rb
|
|
103
|
+
- lib/cf/mcp/models/function_doc.rb
|
|
104
|
+
- lib/cf/mcp/models/struct_doc.rb
|
|
105
|
+
- lib/cf/mcp/models/topic_doc.rb
|
|
106
|
+
- lib/cf/mcp/parser.rb
|
|
107
|
+
- lib/cf/mcp/server.rb
|
|
108
|
+
- lib/cf/mcp/templates/index.erb
|
|
109
|
+
- lib/cf/mcp/templates/script.js
|
|
110
|
+
- lib/cf/mcp/templates/style.css
|
|
111
|
+
- lib/cf/mcp/tools/find_related.rb
|
|
112
|
+
- lib/cf/mcp/tools/get_details.rb
|
|
113
|
+
- lib/cf/mcp/tools/get_topic.rb
|
|
114
|
+
- lib/cf/mcp/tools/list_category.rb
|
|
115
|
+
- lib/cf/mcp/tools/list_topics.rb
|
|
116
|
+
- lib/cf/mcp/tools/member_search.rb
|
|
117
|
+
- lib/cf/mcp/tools/parameter_search.rb
|
|
118
|
+
- lib/cf/mcp/tools/search_enums.rb
|
|
119
|
+
- lib/cf/mcp/tools/search_functions.rb
|
|
120
|
+
- lib/cf/mcp/tools/search_structs.rb
|
|
121
|
+
- lib/cf/mcp/tools/search_tool.rb
|
|
122
|
+
- lib/cf/mcp/topic_parser.rb
|
|
123
|
+
- lib/cf/mcp/version.rb
|
|
124
|
+
- sig/cf/mcp.rbs
|
|
125
|
+
homepage: https://github.com/pusewicz/cf-mcp
|
|
126
|
+
licenses:
|
|
127
|
+
- MIT
|
|
128
|
+
metadata:
|
|
129
|
+
allowed_push_host: https://rubygems.org
|
|
130
|
+
homepage_uri: https://github.com/pusewicz/cf-mcp
|
|
131
|
+
source_code_uri: https://github.com/pusewicz/cf-mcp
|
|
132
|
+
changelog_uri: https://github.com/pusewicz/cf-mcp/blob/main/CHANGELOG.md
|
|
133
|
+
rdoc_options: []
|
|
134
|
+
require_paths:
|
|
135
|
+
- lib
|
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
137
|
+
requirements:
|
|
138
|
+
- - ">="
|
|
139
|
+
- !ruby/object:Gem::Version
|
|
140
|
+
version: 3.2.0
|
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
requirements: []
|
|
147
|
+
rubygems_version: 4.0.3
|
|
148
|
+
specification_version: 4
|
|
149
|
+
summary: MCP server providing documentation tools for Cute Framework
|
|
150
|
+
test_files: []
|