Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
181fd5b
Add MCP server endpoint for AI agent doc access
joebutler2 Sep 3, 2026
37090e8
Optimize devdocs_list_docsets MCP tool for context efficiency
joebutler2 Sep 9, 2026
b4067a0
Add slug validation and error handling for production deployment
joebutler2 Sep 9, 2026
fb18b3a
Fix error handling and unknown tool handling
joebutler2 Sep 12, 2026
4031504
Add input validation against declared tool schemas
joebutler2 Sep 12, 2026
73cd5ed
Fix HTML text extraction to preserve block element separators
joebutler2 Sep 12, 2026
4b313ee
Add pagination and validation to devdocs_search tool
joebutler2 Sep 12, 2026
a8aa4ea
Optimize db.json parsing with in-memory caching
joebutler2 Sep 12, 2026
c5969a1
Add test for JSON parse error handling
joebutler2 Sep 12, 2026
ebe94dc
Refactor error handling in mcp_test.rb
joebutler2 Sep 14, 2026
9225cb1
Merge branch 'main' into feature/mcp-server
simon04 Sep 14, 2026
3a18100
Strip the fragment when looking up a page in db.json
simon04 Sep 14, 2026
3281bfe
Treat tables and definition lists as block elements
simon04 Sep 14, 2026
d402b93
Walk the HTML in document order when extracting text
simon04 Sep 14, 2026
6bbfce0
Keep the whitespace of preformatted text
simon04 Sep 14, 2026
74e052b
Read the page file instead of parsing db.json
simon04 Sep 14, 2026
c8f8527
Cache the parsed search indexes
simon04 Sep 14, 2026
f86dbb6
Drop the unreachable additionalProperties check
simon04 Sep 14, 2026
9a23f2a
Reject non-object payloads with Invalid Request
simon04 Sep 14, 2026
968b01c
Validate the params of tools/call
simon04 Sep 14, 2026
5f6e950
Do not answer JSON-RPC notifications
simon04 Sep 14, 2026
bff3d8a
Report failing tools in the result, not as protocol errors
simon04 Sep 14, 2026
b73e0cd
Assert the missing search index unconditionally
simon04 Sep 14, 2026
dca4caa
Add size-bounded cache with eviction to prevent memory exhaustion
joebutler2 Sep 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Treat tables and definition lists as block elements
Their tags were missing from block_element?, so tables and definition
lists - most of the CSS and HTML reference content - collapsed into runs
like "NameTypefooString".
  • Loading branch information
simon04 committed Sep 14, 2026
commit 3281bfe4834d30ca625abfc603dd913d94fba040
4 changes: 3 additions & 1 deletion lib/mcp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ def self.html_to_text(html)

def self.block_element?(tag_name)
return false unless tag_name
%w(p div h1 h2 h3 h4 h5 h6 ul ol li blockquote pre br).include?(tag_name.downcase)
%w(p div h1 h2 h3 h4 h5 h6 ul ol li dl dt dd
table caption thead tbody tfoot tr th td
blockquote pre br).include?(tag_name.downcase)
end

def self.search_docset(app_settings, slug, query, args = {})
Expand Down
2 changes: 1 addition & 1 deletion test/files/docs/mcp_fixture/db.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"array/push":"<h1>Array#push</h1> <p>Appends &amp; returns the array.</p>","array/pop":"<h1>Array#pop</h1> <p>Removes the last element.</p>"}
{"array/push":"<h1>Array#push</h1> <p>Appends &amp; returns the array.</p>","array/pop":"<h1>Array#pop</h1> <p>Removes the last element.</p>","array/table":"<h1>Attributes</h1> <table><tr><th>Name</th><th>Type</th></tr><tr><td>foo</td><td>String</td></tr></table> <dl><dt>bar</dt><dd>A thing.</dd></dl>"}
9 changes: 9 additions & 0 deletions test/mcp_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,15 @@ def rpc(method, params = nil, id: 1)
assert_includes result['content'].first['text'], 'Removes the last element.'
end

it 'separates table cells and definition lists in the extracted text' do
args = { 'slug' => 'mcp_fixture', 'path' => 'array/table' }
result = rpc('tools/call', { 'name' => 'devdocs_get_page', 'arguments' => args })['result']
text = result['content'].first['text']
refute_includes text, 'NameType'
refute_includes text, 'fooString'
refute_includes text, 'barA thing.'
end

it 'returns error for invalid slug in search (path traversal protection)' do
args = { 'slug' => '../../../etc/passwd', 'query' => 'test' }
response = rpc('tools/call', { 'name' => 'devdocs_search', 'arguments' => args })
Expand Down