cf-mcp 0.10.1 → 0.12.1

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: 4766c9f8bb80d8be199403996163b2e21b7898ea2a8f14e0426d75bbf620364c
4
- data.tar.gz: bd23236c47957a598c6b69c3f7d3d738d40701ceb2dba97b3afc84a94421ccb7
3
+ metadata.gz: 30c68992a64d36c8265af4bdcd018dd116c7d2e59479ab1fd83213896028b86d
4
+ data.tar.gz: 4a4e5b715cdca959296e1043bf3173a2c4c314a40ab5c1cb76154dd20b823557
5
5
  SHA512:
6
- metadata.gz: 1ea224066e2bb647782d8acc1c5c63855b11eafa4b31c6f649e77b74675443d8c4495b029ba6b0b21c4c85b34cf392f562d80c61d1a2d76d53429d6d94ff3732
7
- data.tar.gz: 6158b7385eac465183b837e058042f2d648ab108d5a3b9deacadc7bdbe1e013d3bfec1eca58ecd2df1e2c8628b431a2c168d207035ba2bcd39657c6ae63c7992
6
+ metadata.gz: 1704a19c8b49fb37258b247e5689a15f7ed67a2cce9ff226ee5ac0c4689dbc45b547150d426cc91e15e09a2f36405752f58a98e5b14596e5282dc7b9784c4e15
7
+ data.tar.gz: d1c5c7f81871cddb2f0a1bb81b98272b7d3d07a8d7d40bfafd08c338ef2e6b8073d296407ae2dd74575b7ec2738864c1c5bd941408f9f681e373002af363a70d
data/CHANGELOG.md CHANGED
@@ -5,19 +5,41 @@ 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
+ ## [0.12.1] - 2026-01-14
9
+
10
+ ### Changed
11
+
12
+ - Refactored tool classes to use shared `ResponseHelpers` module, removing duplicate code
13
+ - Extracted `IndexBuilder` class to consolidate index building logic from CLI and HTTPServer
14
+ - Extracted `SearchResultFormatter` module for consistent search result formatting
15
+ - Simplified model `to_text()` methods using template method pattern in `DocItem` base class
16
+
17
+ ## [0.12.0] - 2026-01-14
18
+
19
+ ### Changed
20
+
21
+ - Renamed `CombinedServer` to `HTTPServer` for clarity
22
+ - Updated config.ru to use the new `HTTPServer` class
23
+
24
+ ### Removed
25
+
26
+ - SSE transport endpoint (HTTP streamable transport is now the only HTTP option)
27
+
8
28
  ## [0.10.1] - 2026-01-14
9
29
 
10
30
  ### Added
11
31
 
12
32
  - `--host` CLI option for binding address (defaults to `0.0.0.0`)
33
+ - `CombinedServer.build_rack_app` class method for shared boot logic
13
34
 
14
35
  ### Changed
15
36
 
16
- - Unified boot process: Procfile now uses `cf-mcp combined --download` instead of rackup
37
+ - Unified boot process: config.ru and CLI now share the same initialization logic
38
+ - Simplified config.ru to a single line using `CombinedServer.build_rack_app`
17
39
 
18
40
  ### Removed
19
41
 
20
- - `config.ru` (no longer needed, CLI handles all server modes)
42
+ - `Procfile` (Fly.io uses config.ru directly)
21
43
 
22
44
  ## [0.10.0] - 2026-01-14
23
45
 
@@ -126,6 +148,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
126
148
  - `cf_list_category` - List items by category
127
149
  - `cf_get_details` - Get full documentation by name
128
150
 
151
+ [0.12.1]: https://github.com/pusewicz/cf-mcp/compare/v0.12.0...v0.12.1
152
+ [0.12.0]: https://github.com/pusewicz/cf-mcp/compare/v0.10.1...v0.12.0
129
153
  [0.10.1]: https://github.com/pusewicz/cf-mcp/compare/v0.10.0...v0.10.1
130
154
  [0.10.0]: https://github.com/pusewicz/cf-mcp/compare/v0.9.3...v0.10.0
131
155
  [0.9.3]: https://github.com/pusewicz/cf-mcp/compare/v0.9.2...v0.9.3
data/Manifest.txt CHANGED
@@ -3,11 +3,13 @@ LICENSE.txt
3
3
  Manifest.txt
4
4
  README.md
5
5
  Rakefile
6
+ config.ru
6
7
  exe/cf-mcp
7
8
  lib/cf/mcp.rb
8
9
  lib/cf/mcp/cli.rb
9
10
  lib/cf/mcp/downloader.rb
10
11
  lib/cf/mcp/index.rb
12
+ lib/cf/mcp/index_builder.rb
11
13
  lib/cf/mcp/models/doc_item.rb
12
14
  lib/cf/mcp/models/enum_doc.rb
13
15
  lib/cf/mcp/models/function_doc.rb
@@ -25,8 +27,10 @@ lib/cf/mcp/tools/list_category.rb
25
27
  lib/cf/mcp/tools/list_topics.rb
26
28
  lib/cf/mcp/tools/member_search.rb
27
29
  lib/cf/mcp/tools/parameter_search.rb
30
+ lib/cf/mcp/tools/response_helpers.rb
28
31
  lib/cf/mcp/tools/search_enums.rb
29
32
  lib/cf/mcp/tools/search_functions.rb
33
+ lib/cf/mcp/tools/search_result_formatter.rb
30
34
  lib/cf/mcp/tools/search_structs.rb
31
35
  lib/cf/mcp/tools/search_tool.rb
32
36
  lib/cf/mcp/topic_parser.rb
data/README.md CHANGED
@@ -2,11 +2,10 @@
2
2
 
3
3
  CF::MCP is an MCP server providing documentation tools for the [Cute Framework](https://github.com/RandyGaul/cute_framework), a C/C++ 2D game framework.
4
4
 
5
- The MCP server supports three modes of operation:
5
+ The MCP server supports two modes of operation:
6
6
 
7
7
  - **STDIO Mode**: Communicates via standard input and output streams, suitable for integration with CLI tools and desktop applications.
8
- - **HTTP Mode**: Operates as a stateless HTTP server, suitable for simple request/response interactions and multi-node deployments.
9
- - **SSE Mode**: Operates as a stateful HTTP server with Server-Sent Events support, enabling real-time notifications and streaming responses.
8
+ - **HTTP Mode**: Operates as an HTTP server with a web interface at the root and MCP endpoint at `/http`.
10
9
 
11
10
  ## Features
12
11
 
@@ -33,8 +32,7 @@ To start the MCP server, run the following command in your terminal:
33
32
 
34
33
  ```bash
35
34
  cf-mcp stdio --root /path/to/cute_framework_project # STDIO mode
36
- cf-mcp http --root /path/to/cute_framework_project # HTTP mode (stateless)
37
- cf-mcp sse --root /path/to/cute_framework_project # SSE mode (stateful, real-time)
35
+ cf-mcp http --root /path/to/cute_framework_project # HTTP mode with web UI
38
36
  ```
39
37
 
40
38
  ## Development
data/config.ru ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/cf/mcp"
4
+
5
+ # Build and run the HTTP server with automatic header downloading
6
+ run CF::MCP::HTTPServer.build_rack_app(download: true)
data/lib/cf/mcp/cli.rb CHANGED
@@ -1,17 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "optparse"
4
- require_relative "parser"
5
- require_relative "topic_parser"
6
- require_relative "index"
4
+ require_relative "index_builder"
7
5
  require_relative "server"
8
- require_relative "downloader"
9
6
 
10
7
  module CF
11
8
  module MCP
12
9
  class CLI
13
- DEFAULT_HEADERS_PATH = File.expand_path("~/Work/GitHub/pusewicz/cute_framework/include")
14
-
15
10
  def initialize(args)
16
11
  @args = args
17
12
  @options = parse_args
@@ -22,11 +17,7 @@ module CF
22
17
  when :stdio
23
18
  run_server(:stdio)
24
19
  when :http
25
- run_server(:http)
26
- when :sse
27
- run_server(:sse)
28
- when :combined
29
- run_combined_server
20
+ run_http_server
30
21
  when :help
31
22
  puts @option_parser
32
23
  else
@@ -51,9 +42,7 @@ module CF
51
42
  opts.separator ""
52
43
  opts.separator "Commands:"
53
44
  opts.separator " stdio Run in STDIO mode (for CLI integration)"
54
- opts.separator " http Run as HTTP server (stateless)"
55
- opts.separator " sse Run as SSE server (stateful with real-time updates)"
56
- opts.separator " combined Run as combined server (SSE + HTTP with web interface)"
45
+ opts.separator " http Run as HTTP server with web interface"
57
46
  opts.separator ""
58
47
  opts.separator "Options:"
59
48
 
@@ -61,7 +50,7 @@ module CF
61
50
  options[:root] = path
62
51
  end
63
52
 
64
- opts.on("-p", "--port PORT", Integer, "Port for HTTP/SSE server (default: 9292 for HTTP, 9393 for SSE)") do |port|
53
+ opts.on("-p", "--port PORT", Integer, "Port for HTTP server (default: 9292)") do |port|
65
54
  options[:port] = port
66
55
  end
67
56
 
@@ -88,7 +77,7 @@ module CF
88
77
  # Parse command from remaining args
89
78
  if options[:command].nil? && !@args.empty?
90
79
  command = @args.shift.to_sym
91
- options[:command] = command if [:stdio, :http, :sse, :combined].include?(command)
80
+ options[:command] = command if [:stdio, :http].include?(command)
92
81
  end
93
82
 
94
83
  options[:command] ||= :help
@@ -96,122 +85,41 @@ module CF
96
85
  end
97
86
 
98
87
  def run_server(mode)
99
- headers_path = resolve_headers_path
88
+ builder = IndexBuilder.new(root: @options[:root], download: @options[:download])
100
89
 
101
- unless File.directory?(headers_path)
102
- warn "Error: Headers directory not found: #{headers_path}"
90
+ unless builder.valid?
91
+ warn "Error: Headers directory not found: #{builder.headers_path}"
103
92
  warn "Use --root to specify the path to Cute Framework headers"
104
93
  warn "Or use --download to fetch headers from GitHub"
105
94
  exit 1
106
95
  end
107
96
 
108
- warn "Parsing headers from: #{headers_path}"
109
- index = build_index(headers_path)
97
+ warn "Parsing headers from: #{builder.headers_path}"
98
+ index = builder.build do |event, path, count|
99
+ warn "Indexed #{count} topics from: #{path}" if event == :topics_indexed
100
+ end
110
101
  warn "Indexed #{index.stats[:total]} items (#{index.stats[:functions]} functions, #{index.stats[:structs]} structs, #{index.stats[:enums]} enums)"
111
102
 
112
103
  server = Server.new(index)
113
-
114
- case mode
115
- when :stdio
116
- server.run_stdio
117
- when :http
118
- port = @options[:port] || 9292
119
- server.run_http(port: port)
120
- when :sse
121
- port = @options[:port] || 9393
122
- server.run_sse(port: port)
123
- end
104
+ server.run_stdio
124
105
  end
125
106
 
126
- def run_combined_server
127
- require "rack"
107
+ def run_http_server
128
108
  require "rackup"
129
109
 
130
- headers_path = resolve_headers_path
131
-
132
- unless File.directory?(headers_path)
133
- warn "Error: Headers directory not found: #{headers_path}"
134
- warn "Use --root to specify the path to Cute Framework headers"
135
- warn "Or use --download to fetch headers from GitHub"
136
- exit 1
137
- end
138
-
139
- warn "Parsing headers from: #{headers_path}"
140
- index = build_index(headers_path)
141
- warn "Indexed #{index.stats[:total]} items (#{index.stats[:functions]} functions, #{index.stats[:structs]} structs, #{index.stats[:enums]} enums)"
142
-
143
110
  port = @options[:port] || 9292
144
111
  host = @options[:host]
145
- server = CombinedServer.new(index)
146
- app = server.rack_app
147
112
 
148
- warn "Starting combined server on #{host}:#{port}..."
113
+ app = HTTPServer.build_rack_app(
114
+ root: @options[:root],
115
+ download: @options[:download]
116
+ )
117
+
118
+ warn "Starting HTTP server on #{host}:#{port}..."
149
119
  warn "Web interface available at http://localhost:#{port}/"
120
+ warn "MCP endpoint available at http://localhost:#{port}/http"
150
121
  Rackup::Server.start(app: app, Host: host, Port: port, Logger: $stderr)
151
122
  end
152
-
153
- def resolve_headers_path
154
- return @options[:root] if @options[:root]
155
- return ENV["CF_HEADERS_PATH"] if ENV["CF_HEADERS_PATH"]
156
-
157
- if @options[:download]
158
- warn "Downloading Cute Framework headers from GitHub..."
159
- downloader = Downloader.new
160
- path = downloader.download_and_extract
161
- warn "Downloaded headers to: #{path}"
162
- return path
163
- end
164
-
165
- DEFAULT_HEADERS_PATH
166
- end
167
-
168
- def build_index(headers_path)
169
- parser = Parser.new
170
- index = Index.new
171
-
172
- parser.parse_directory(headers_path).each do |item|
173
- index.add(item)
174
- end
175
-
176
- # Parse topics if available
177
- topics_path = find_topics_path(headers_path)
178
- if topics_path && File.directory?(topics_path)
179
- topic_parser = TopicParser.new
180
- topic_parser.parse_directory(topics_path).each do |topic|
181
- refine_topic_references(topic, index)
182
- index.add(topic)
183
- end
184
- warn "Indexed #{index.stats[:topics]} topics from: #{topics_path}"
185
- end
186
-
187
- index
188
- end
189
-
190
- def find_topics_path(headers_path)
191
- # If headers_path is .../cute_framework/include, topics is at .../cute_framework/docs/topics
192
- base = File.dirname(headers_path)
193
- topics_path = File.join(base, "docs", "topics")
194
- return topics_path if File.directory?(topics_path)
195
-
196
- # Alternative: topics directly under headers parent
197
- topics_path = File.join(base, "topics")
198
- return topics_path if File.directory?(topics_path)
199
-
200
- nil
201
- end
202
-
203
- def refine_topic_references(topic, index)
204
- # Move items from struct_references to enum_references if they're actually enums
205
- topic.struct_references.dup.each do |ref|
206
- item = index.find(ref)
207
- next unless item
208
-
209
- if item.type == :enum
210
- topic.struct_references.delete(ref)
211
- topic.enum_references << ref unless topic.enum_references.include?(ref)
212
- end
213
- end
214
- end
215
123
  end
216
124
  end
217
125
  end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "parser"
4
+ require_relative "topic_parser"
5
+ require_relative "index"
6
+ require_relative "downloader"
7
+
8
+ module CF
9
+ module MCP
10
+ class IndexBuilder
11
+ DEFAULT_HEADERS_PATH = File.expand_path("~/Work/GitHub/pusewicz/cute_framework/include")
12
+
13
+ attr_reader :headers_path
14
+
15
+ def initialize(root: nil, download: false)
16
+ @headers_path = resolve_headers_path(root: root, download: download)
17
+ end
18
+
19
+ def build
20
+ parser = Parser.new
21
+ index = Index.new
22
+
23
+ parser.parse_directory(headers_path).each do |item|
24
+ index.add(item)
25
+ end
26
+
27
+ # Parse topics if available
28
+ topics_path = find_topics_path(headers_path)
29
+ if topics_path && File.directory?(topics_path)
30
+ topic_parser = TopicParser.new
31
+ topic_parser.parse_directory(topics_path).each do |topic|
32
+ refine_topic_references(topic, index)
33
+ index.add(topic)
34
+ end
35
+ yield(:topics_indexed, topics_path, index.stats[:topics]) if block_given?
36
+ end
37
+
38
+ index
39
+ end
40
+
41
+ def valid?
42
+ File.directory?(headers_path)
43
+ end
44
+
45
+ private
46
+
47
+ def resolve_headers_path(root:, download:)
48
+ return root if root
49
+ return ENV["CF_HEADERS_PATH"] if ENV["CF_HEADERS_PATH"]
50
+
51
+ if download
52
+ warn "Downloading Cute Framework headers from GitHub..."
53
+ downloader = Downloader.new
54
+ path = downloader.download_and_extract
55
+ warn "Downloaded headers to: #{path}"
56
+ return path
57
+ end
58
+
59
+ DEFAULT_HEADERS_PATH
60
+ end
61
+
62
+ def find_topics_path(headers_path)
63
+ # If headers_path is .../cute_framework/include, topics is at .../cute_framework/docs/topics
64
+ base = File.dirname(headers_path)
65
+ topics_path = File.join(base, "docs", "topics")
66
+ return topics_path if File.directory?(topics_path)
67
+
68
+ # Alternative: topics directly under headers parent
69
+ topics_path = File.join(base, "topics")
70
+ return topics_path if File.directory?(topics_path)
71
+
72
+ nil
73
+ end
74
+
75
+ def refine_topic_references(topic, index)
76
+ # Move items from struct_references to enum_references if they're actually enums
77
+ topic.struct_references.dup.each do |ref|
78
+ item = index.find(ref)
79
+ next unless item
80
+
81
+ if item.type == :enum
82
+ topic.struct_references.delete(ref)
83
+ topic.enum_references << ref unless topic.enum_references.include?(ref)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -128,6 +128,23 @@ module CF
128
128
  end
129
129
 
130
130
  def to_text(detailed: false, index: nil)
131
+ lines = []
132
+ lines.concat(build_header_lines)
133
+ lines.concat(build_description_lines)
134
+
135
+ if detailed
136
+ lines.concat(build_type_specific_lines)
137
+ lines.concat(build_remarks_lines)
138
+ lines.concat(build_example_lines)
139
+ lines.concat(build_related_lines(index))
140
+ end
141
+
142
+ lines.join("\n")
143
+ end
144
+
145
+ protected
146
+
147
+ def build_header_lines
131
148
  lines = []
132
149
  lines << "# #{name}"
133
150
  lines << ""
@@ -140,36 +157,44 @@ module CF
140
157
  lines << "- **Implementation:** #{urls[:impl_raw]}"
141
158
  end
142
159
  lines << ""
160
+ lines
161
+ end
162
+
163
+ def build_description_lines
164
+ lines = []
143
165
  lines << "## Description"
144
166
  lines << brief if brief
145
167
  lines << ""
168
+ lines
169
+ end
146
170
 
147
- if detailed
148
- if remarks && !remarks.empty?
149
- lines << "## Remarks"
150
- lines << remarks
151
- lines << ""
152
- end
171
+ def build_type_specific_lines
172
+ [] # Override in subclasses
173
+ end
153
174
 
154
- if example && !example.empty?
155
- lines << "## Example"
156
- lines << example_brief if example_brief
157
- lines << "```c"
158
- lines << example
159
- lines << "```"
160
- lines << ""
161
- end
175
+ def build_remarks_lines
176
+ return [] unless remarks && !remarks.empty?
177
+ ["## Remarks", remarks, ""]
178
+ end
162
179
 
163
- if related && !related.empty?
164
- lines << "## Related"
165
- lines << format_related_items(index)
166
- lines << ""
167
- end
168
- end
180
+ def build_example_lines
181
+ return [] unless example && !example.empty?
182
+ lines = ["## Example"]
183
+ lines << example_brief if example_brief
184
+ lines << "```c"
185
+ lines << example
186
+ lines << "```"
187
+ lines << ""
188
+ lines
189
+ end
169
190
 
170
- lines.join("\n")
191
+ def build_related_lines(index)
192
+ return [] unless related && !related.empty?
193
+ ["## Related", format_related_items(index), ""]
171
194
  end
172
195
 
196
+ public
197
+
173
198
  def format_related_items(index)
174
199
  return related.join(", ") unless index
175
200
 
@@ -24,58 +24,21 @@ module CF
24
24
  ).compact
25
25
  end
26
26
 
27
- def to_text(detailed: false, index: nil)
27
+ protected
28
+
29
+ def build_type_specific_lines
30
+ return [] unless entries && !entries.empty?
31
+
28
32
  lines = []
29
- lines << "# #{name}"
33
+ lines << "## Values"
30
34
  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]}"
35
+ lines << "| Name | Value | Description |"
36
+ lines << "| --- | --- | --- |"
37
+ entries.each do |entry|
38
+ lines << "| `#{entry.name}` | #{entry.value} | #{entry.description} |"
38
39
  end
39
40
  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")
41
+ lines
79
42
  end
80
43
  end
81
44
  end
@@ -38,71 +38,48 @@ module CF
38
38
 
39
39
  def to_text(detailed: false, index: nil)
40
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 << ""
41
+ lines.concat(build_header_lines)
42
+ lines.concat(build_signature_lines)
43
+ lines.concat(build_description_lines)
52
44
 
53
- if signature
54
- lines << "## Signature"
55
- lines << "```c"
56
- lines << signature
57
- lines << "```"
58
- lines << ""
45
+ if detailed
46
+ lines.concat(build_type_specific_lines)
47
+ lines.concat(build_remarks_lines)
48
+ lines.concat(build_example_lines)
49
+ lines.concat(build_related_lines(index))
59
50
  end
60
51
 
61
- lines << "## Description"
62
- lines << brief if brief
63
- lines << ""
52
+ lines.join("\n")
53
+ end
64
54
 
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
55
+ protected
76
56
 
77
- if return_value && !return_value.empty?
78
- lines << "## Return Value"
79
- lines << return_value
80
- lines << ""
81
- end
57
+ def build_signature_lines
58
+ return [] unless signature
59
+ ["## Signature", "```c", signature, "```", ""]
60
+ end
82
61
 
83
- if remarks && !remarks.empty?
84
- lines << "## Remarks"
85
- lines << remarks
86
- lines << ""
87
- end
62
+ def build_type_specific_lines
63
+ lines = []
88
64
 
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 << ""
65
+ if parameters && !parameters.empty?
66
+ lines << "## Parameters"
67
+ lines << ""
68
+ lines << "| Parameter | Description |"
69
+ lines << "| --- | --- |"
70
+ parameters.each do |param|
71
+ lines << "| `#{param.name}` | #{param.description} |"
96
72
  end
73
+ lines << ""
74
+ end
97
75
 
98
- if related && !related.empty?
99
- lines << "## Related"
100
- lines << format_related_items(index)
101
- lines << ""
102
- end
76
+ if return_value && !return_value.empty?
77
+ lines << "## Return Value"
78
+ lines << return_value
79
+ lines << ""
103
80
  end
104
81
 
105
- lines.join("\n")
82
+ lines
106
83
  end
107
84
  end
108
85
  end