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.
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CF
4
+ module MCP
5
+ module Models
6
+ class DocItem
7
+ GITHUB_REPO = "https://github.com/RandyGaul/cute_framework"
8
+ GITHUB_RAW_BASE = "https://raw.githubusercontent.com/RandyGaul/cute_framework/refs/heads/master"
9
+
10
+ attr_accessor :name, :type, :category, :brief, :remarks, :example,
11
+ :example_brief, :related, :source_file, :source_line
12
+
13
+ def initialize(
14
+ name: nil,
15
+ type: nil,
16
+ category: nil,
17
+ brief: nil,
18
+ remarks: nil,
19
+ example: nil,
20
+ example_brief: nil,
21
+ related: [],
22
+ source_file: nil,
23
+ source_line: nil
24
+ )
25
+ @name = name
26
+ @type = type
27
+ @category = category
28
+ @brief = brief
29
+ @remarks = remarks
30
+ @example = example
31
+ @example_brief = example_brief
32
+ @related = related || []
33
+ @source_file = source_file
34
+ @source_line = source_line
35
+ end
36
+
37
+ def matches?(query)
38
+ return true if query.nil? || query.empty?
39
+
40
+ pattern = Regexp.new(Regexp.escape(query), Regexp::IGNORECASE)
41
+ [name, brief, remarks, category].any? { |field| field&.match?(pattern) }
42
+ end
43
+
44
+ # Returns a relevance score for ranking search results.
45
+ # Higher scores indicate better matches.
46
+ def relevance_score(query)
47
+ return 0 if query.nil? || query.empty?
48
+
49
+ score = 0
50
+ query_downcase = query.downcase
51
+ name_downcase = name&.downcase || ""
52
+
53
+ # Exact name match (highest priority)
54
+ if name_downcase == query_downcase
55
+ score += 1000
56
+ # Name starts with query (prefix match)
57
+ elsif name_downcase.start_with?(query_downcase)
58
+ score += 500
59
+ # Name ends with query (suffix match, e.g., "make_app" matches "cf_make_app")
60
+ elsif name_downcase.end_with?(query_downcase)
61
+ score += 400
62
+ # Name contains query
63
+ elsif name_downcase.include?(query_downcase)
64
+ score += 100
65
+ end
66
+
67
+ # Brief contains query
68
+ score += 50 if brief&.downcase&.include?(query_downcase)
69
+
70
+ # Category contains query
71
+ score += 30 if category&.downcase&.include?(query_downcase)
72
+
73
+ # Remarks contains query
74
+ score += 10 if remarks&.downcase&.include?(query_downcase)
75
+
76
+ score
77
+ end
78
+
79
+ def source_urls
80
+ return nil unless source_file
81
+ # Header file URLs (include/cute_xxx.h)
82
+ header_path = "include/#{source_file}"
83
+ raw = "#{GITHUB_RAW_BASE}/#{header_path}"
84
+ blob = "#{GITHUB_REPO}/blob/master/#{header_path}"
85
+ blob += "#L#{source_line}" if source_line
86
+
87
+ # Implementation file URL (src/cute_xxx.cpp)
88
+ impl_file = source_file.sub(/\.h$/, ".cpp")
89
+ impl_raw = "#{GITHUB_RAW_BASE}/src/#{impl_file}"
90
+
91
+ {raw: raw, blob: blob, impl_raw: impl_raw}
92
+ end
93
+
94
+ def to_h
95
+ {
96
+ name: name,
97
+ type: type,
98
+ category: category,
99
+ brief: brief,
100
+ remarks: remarks,
101
+ example: example,
102
+ example_brief: example_brief,
103
+ related: related,
104
+ source_file: source_file,
105
+ source_line: source_line
106
+ }.compact
107
+ end
108
+
109
+ def to_summary
110
+ "- **#{name}** `(#{type}, #{category})` — #{brief}"
111
+ end
112
+
113
+ def to_text(detailed: false, index: nil)
114
+ lines = []
115
+ lines << "# #{name}"
116
+ lines << ""
117
+ lines << "- **Type:** #{type}"
118
+ lines << "- **Category:** #{category}" if category
119
+ if source_file
120
+ urls = source_urls
121
+ lines << "- **Source:** [include/#{source_file}](#{urls[:blob]})"
122
+ lines << "- **Raw:** #{urls[:raw]}"
123
+ lines << "- **Implementation:** #{urls[:impl_raw]}"
124
+ end
125
+ lines << ""
126
+ lines << "## Description"
127
+ lines << brief if brief
128
+ lines << ""
129
+
130
+ if detailed
131
+ if remarks && !remarks.empty?
132
+ lines << "## Remarks"
133
+ lines << remarks
134
+ lines << ""
135
+ end
136
+
137
+ if example && !example.empty?
138
+ lines << "## Example"
139
+ lines << example_brief if example_brief
140
+ lines << "```c"
141
+ lines << example
142
+ lines << "```"
143
+ lines << ""
144
+ end
145
+
146
+ if related && !related.empty?
147
+ lines << "## Related"
148
+ lines << format_related_items(index)
149
+ lines << ""
150
+ end
151
+ end
152
+
153
+ lines.join("\n")
154
+ end
155
+
156
+ def format_related_items(index)
157
+ return related.join(", ") unless index
158
+
159
+ related.map do |rel_name|
160
+ info = index.brief_for(rel_name)
161
+ if info
162
+ "- `#{info[:name]}` (#{info[:type]}) — #{info[:brief]}"
163
+ else
164
+ "- `#{rel_name}`"
165
+ end
166
+ end.join("\n")
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "doc_item"
4
+
5
+ module CF
6
+ module MCP
7
+ module Models
8
+ class EnumDoc < DocItem
9
+ attr_accessor :entries
10
+
11
+ Entry = Data.define(:name, :value, :description)
12
+
13
+ def initialize(
14
+ entries: [],
15
+ **kwargs
16
+ )
17
+ super(type: :enum, **kwargs)
18
+ @entries = entries || []
19
+ end
20
+
21
+ def to_h
22
+ super.merge(
23
+ entries: entries.map { |e| {name: e.name, value: e.value, description: e.description} }
24
+ ).compact
25
+ end
26
+
27
+ def to_text(detailed: false, index: nil)
28
+ lines = []
29
+ lines << "# #{name}"
30
+ lines << ""
31
+ lines << "- **Type:** enum"
32
+ lines << "- **Category:** #{category}" if category
33
+ if source_file
34
+ urls = source_urls
35
+ lines << "- **Source:** [include/#{source_file}](#{urls[:blob]})"
36
+ lines << "- **Raw:** #{urls[:raw]}"
37
+ lines << "- **Implementation:** #{urls[:impl_raw]}"
38
+ end
39
+ lines << ""
40
+ lines << "## Description"
41
+ lines << brief if brief
42
+ lines << ""
43
+
44
+ if detailed
45
+ if entries && !entries.empty?
46
+ lines << "## Values"
47
+ lines << ""
48
+ lines << "| Name | Value | Description |"
49
+ lines << "| --- | --- | --- |"
50
+ entries.each do |entry|
51
+ lines << "| `#{entry.name}` | #{entry.value} | #{entry.description} |"
52
+ end
53
+ lines << ""
54
+ end
55
+
56
+ if remarks && !remarks.empty?
57
+ lines << "## Remarks"
58
+ lines << remarks
59
+ lines << ""
60
+ end
61
+
62
+ if example && !example.empty?
63
+ lines << "## Example"
64
+ lines << example_brief if example_brief
65
+ lines << "```c"
66
+ lines << example
67
+ lines << "```"
68
+ lines << ""
69
+ end
70
+
71
+ if related && !related.empty?
72
+ lines << "## Related"
73
+ lines << format_related_items(index)
74
+ lines << ""
75
+ end
76
+ end
77
+
78
+ lines.join("\n")
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "doc_item"
4
+
5
+ module CF
6
+ module MCP
7
+ module Models
8
+ class FunctionDoc < DocItem
9
+ attr_accessor :signature, :parameters, :return_value
10
+
11
+ Parameter = Data.define(:name, :description)
12
+
13
+ def initialize(
14
+ signature: nil,
15
+ parameters: [],
16
+ return_value: nil,
17
+ **kwargs
18
+ )
19
+ super(type: :function, **kwargs)
20
+ @signature = signature
21
+ @parameters = parameters || []
22
+ @return_value = return_value
23
+ end
24
+
25
+ def to_h
26
+ super.merge(
27
+ signature: signature,
28
+ parameters: parameters.map { |p| {name: p.name, description: p.description} },
29
+ return_value: return_value
30
+ ).compact
31
+ end
32
+
33
+ def to_summary
34
+ lines = ["- **#{name}** `(#{type}, #{category})` — #{brief}"]
35
+ lines << " `#{signature}`" if signature
36
+ lines.join("\n")
37
+ end
38
+
39
+ def to_text(detailed: false, index: nil)
40
+ lines = []
41
+ lines << "# #{name}"
42
+ lines << ""
43
+ lines << "- **Type:** function"
44
+ lines << "- **Category:** #{category}" if category
45
+ if source_file
46
+ urls = source_urls
47
+ lines << "- **Source:** [include/#{source_file}](#{urls[:blob]})"
48
+ lines << "- **Raw:** #{urls[:raw]}"
49
+ lines << "- **Implementation:** #{urls[:impl_raw]}"
50
+ end
51
+ lines << ""
52
+
53
+ if signature
54
+ lines << "## Signature"
55
+ lines << "```c"
56
+ lines << signature
57
+ lines << "```"
58
+ lines << ""
59
+ end
60
+
61
+ lines << "## Description"
62
+ lines << brief if brief
63
+ lines << ""
64
+
65
+ if detailed
66
+ if parameters && !parameters.empty?
67
+ lines << "## Parameters"
68
+ lines << ""
69
+ lines << "| Parameter | Description |"
70
+ lines << "| --- | --- |"
71
+ parameters.each do |param|
72
+ lines << "| `#{param.name}` | #{param.description} |"
73
+ end
74
+ lines << ""
75
+ end
76
+
77
+ if return_value && !return_value.empty?
78
+ lines << "## Return Value"
79
+ lines << return_value
80
+ lines << ""
81
+ end
82
+
83
+ if remarks && !remarks.empty?
84
+ lines << "## Remarks"
85
+ lines << remarks
86
+ lines << ""
87
+ end
88
+
89
+ if example && !example.empty?
90
+ lines << "## Example"
91
+ lines << example_brief if example_brief
92
+ lines << "```c"
93
+ lines << example
94
+ lines << "```"
95
+ lines << ""
96
+ end
97
+
98
+ if related && !related.empty?
99
+ lines << "## Related"
100
+ lines << format_related_items(index)
101
+ lines << ""
102
+ end
103
+ end
104
+
105
+ lines.join("\n")
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "doc_item"
4
+
5
+ module CF
6
+ module MCP
7
+ module Models
8
+ class StructDoc < DocItem
9
+ attr_accessor :members
10
+
11
+ Member = Data.define(:declaration, :description)
12
+
13
+ def initialize(
14
+ members: [],
15
+ **kwargs
16
+ )
17
+ super(type: :struct, **kwargs)
18
+ @members = members || []
19
+ end
20
+
21
+ def to_h
22
+ super.merge(
23
+ members: members.map { |m| {declaration: m.declaration, description: m.description} }
24
+ ).compact
25
+ end
26
+
27
+ def to_text(detailed: false, index: nil)
28
+ lines = []
29
+ lines << "# #{name}"
30
+ lines << ""
31
+ lines << "- **Type:** struct"
32
+ lines << "- **Category:** #{category}" if category
33
+ if source_file
34
+ urls = source_urls
35
+ lines << "- **Source:** [include/#{source_file}](#{urls[:blob]})"
36
+ lines << "- **Raw:** #{urls[:raw]}"
37
+ lines << "- **Implementation:** #{urls[:impl_raw]}"
38
+ end
39
+ lines << ""
40
+ lines << "## Description"
41
+ lines << brief if brief
42
+ lines << ""
43
+
44
+ if detailed
45
+ if members && !members.empty?
46
+ lines << "## Members"
47
+ lines << ""
48
+ lines << "| Member | Description |"
49
+ lines << "| --- | --- |"
50
+ members.each do |member|
51
+ lines << "| `#{member.declaration}` | #{member.description} |"
52
+ end
53
+ lines << ""
54
+ end
55
+
56
+ if remarks && !remarks.empty?
57
+ lines << "## Remarks"
58
+ lines << remarks
59
+ lines << ""
60
+ end
61
+
62
+ if example && !example.empty?
63
+ lines << "## Example"
64
+ lines << example_brief if example_brief
65
+ lines << "```c"
66
+ lines << example
67
+ lines << "```"
68
+ lines << ""
69
+ end
70
+
71
+ if related && !related.empty?
72
+ lines << "## Related"
73
+ lines << format_related_items(index)
74
+ lines << ""
75
+ end
76
+ end
77
+
78
+ lines.join("\n")
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CF
4
+ module MCP
5
+ module Models
6
+ class TopicDoc < DocItem
7
+ attr_accessor :content, :sections, :function_references, :struct_references,
8
+ :enum_references, :topic_references, :reading_order
9
+
10
+ Section = Data.define(:title, :content)
11
+
12
+ def initialize(
13
+ content: nil,
14
+ sections: [],
15
+ function_references: [],
16
+ struct_references: [],
17
+ enum_references: [],
18
+ topic_references: [],
19
+ reading_order: nil,
20
+ **kwargs
21
+ )
22
+ super(type: :topic, **kwargs)
23
+ @content = content
24
+ @sections = sections || []
25
+ @function_references = function_references || []
26
+ @struct_references = struct_references || []
27
+ @enum_references = enum_references || []
28
+ @topic_references = topic_references || []
29
+ @reading_order = reading_order
30
+ end
31
+
32
+ def all_api_references
33
+ function_references + struct_references + enum_references
34
+ end
35
+
36
+ def to_h
37
+ super.merge(
38
+ content: content,
39
+ sections: sections.map { |s| {title: s.title, content: s.content} },
40
+ function_references: function_references,
41
+ struct_references: struct_references,
42
+ enum_references: enum_references,
43
+ topic_references: topic_references,
44
+ reading_order: reading_order
45
+ ).compact
46
+ end
47
+
48
+ def to_summary
49
+ "- **#{name}** `(topic)` — #{brief}"
50
+ end
51
+
52
+ def to_text(detailed: false, index: nil)
53
+ lines = []
54
+ lines << "# #{name}"
55
+ lines << ""
56
+ lines << "**Type:** topic"
57
+ lines << "**Category:** #{category}" if category
58
+ lines << "**Source:** #{source_file}" if source_file
59
+ lines << ""
60
+ lines << "## Overview"
61
+ lines << brief if brief
62
+ lines << ""
63
+
64
+ if detailed
65
+ lines << "## Content"
66
+ lines << ""
67
+ lines << content if content
68
+ lines << ""
69
+
70
+ if function_references.any?
71
+ lines << "## Referenced Functions"
72
+ lines << format_api_references(function_references, index)
73
+ lines << ""
74
+ end
75
+
76
+ if struct_references.any?
77
+ lines << "## Referenced Structs"
78
+ lines << format_api_references(struct_references, index)
79
+ lines << ""
80
+ end
81
+
82
+ if enum_references.any?
83
+ lines << "## Referenced Enums"
84
+ lines << format_api_references(enum_references, index)
85
+ lines << ""
86
+ end
87
+
88
+ if topic_references.any?
89
+ lines << "## Related Topics"
90
+ lines << topic_references.map { |t| "- #{t}" }.join("\n")
91
+ lines << ""
92
+ end
93
+ end
94
+
95
+ lines.join("\n")
96
+ end
97
+
98
+ private
99
+
100
+ def format_api_references(refs, index)
101
+ refs.map do |ref_name|
102
+ if index
103
+ info = index.brief_for(ref_name)
104
+ info ? "- `#{info[:name]}` — #{info[:brief]}" : "- `#{ref_name}`"
105
+ else
106
+ "- `#{ref_name}`"
107
+ end
108
+ end.join("\n")
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end