<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Katsu</title>
    <description>The latest articles on DEV Community by Katsu (@katsukatsu).</description>
    <link>https://dev.to/katsukatsu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3545334%2Fec7bf843-ab5f-4b49-9e89-ef11dc9a9793.png</url>
      <title>DEV Community: Katsu</title>
      <link>https://dev.to/katsukatsu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/katsukatsu"/>
    <language>en</language>
    <item>
      <title>MCP Server for Accessing Official Language Docs from AI Editors</title>
      <dc:creator>Katsu</dc:creator>
      <pubDate>Sat, 04 Oct 2025 15:39:40 +0000</pubDate>
      <link>https://dev.to/katsukatsu/mcp-server-for-accessing-official-language-docs-from-ai-editors-2d3i</link>
      <guid>https://dev.to/katsukatsu/mcp-server-for-accessing-official-language-docs-from-ai-editors-2d3i</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;There is an OSS called DevDocs that aggregates official documentation for various programming languages. I built an MCP (Model Context Protocol) server that makes DevDocs accessible from AI editors (such as Claude or Cursor). Both DevDocs and the MCP server run entirely in a local environment.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/katsulau/devdocs-mcp" rel="noopener noreferrer"&gt;https://github.com/katsulau/devdocs-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The overall architecture looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbu262i6tn9mxposvqmk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbu262i6tn9mxposvqmk.png" alt="architecture" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built This
&lt;/h2&gt;

&lt;h3&gt;
  
  
  To learn new languages more efficiently
&lt;/h3&gt;

&lt;p&gt;If I can provide the official documentation as the source, I can acquire knowledge from a reliable resource.&lt;/p&gt;

&lt;h3&gt;
  
  
  To validate the correctness of implementations
&lt;/h3&gt;

&lt;p&gt;Instead of relying solely on answers from Claude or Cursor, I can make more informed decisions by coding while referencing the official documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  To expand implementation options
&lt;/h3&gt;

&lt;p&gt;By checking the primary source (references), I can consider more diverse ways of writing code and design choices.&lt;br&gt;
While using Claude or Cursor has reduced the effort of searching and implementing things myself, I feel it has also decreased the opportunities to design and make appropriate implementation choices on my own.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
    1.  Use Claude or Cursor to proceed with an implementation&lt;br&gt;
    2.  Verify the generated code via MCP&lt;br&gt;
    3.  Get answers with DevDocs links included&lt;br&gt;
    4.  If necessary, open the links and read the references&lt;/p&gt;

&lt;p&gt;By seamlessly referencing official documentation, I can supplement my knowledge about which use cases a feature applies to, while broadening my own design and implementation perspectives. Building this kind of UX was also one of my motivations.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Use
&lt;/h3&gt;

&lt;p&gt;The repository is here.&lt;br&gt;
👉 &lt;a href="https://github.com/katsulau/devdocs-mcp" rel="noopener noreferrer"&gt;https://github.com/katsulau/devdocs-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please refer to the README for setup instructions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Demo
&lt;/h4&gt;

&lt;p&gt;Here is an example screen when used with Cursor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmnya7ou3hwhwxcjm9k5k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmnya7ou3hwhwxcjm9k5k.gif" alt="devdocs mcp demo" width="480" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking the link will open the corresponding page in your local DevDocs instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fal0x41ewbqhv6omp42pe.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fal0x41ewbqhv6omp42pe.gif" alt="click mcp demo" width="480" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned from Building the MCP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Accuracy improves when reducing ambiguity in inputs
&lt;/h3&gt;

&lt;p&gt;Even if you provide the exact same input to AI editors like Cursor or Claude, they won’t always return the same output *1.&lt;/p&gt;

&lt;p&gt;At first, I considered automatically detecting the programming language from the user’s input, but encountered these issues:&lt;br&gt;
    • It took extra time to extract the target language for search&lt;br&gt;
    • Depending on the query, the target might not be detected correctly&lt;/p&gt;

&lt;p&gt;So I switched to a method where the slash command directly specifies parameters. This greatly improved both stability and accuracy.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;h3&gt;
  
  
  Leave output generation to the AI editor where possible
&lt;/h3&gt;

&lt;p&gt;I also prepared an endpoint where users can check whether the language they want exists in DevDocs.&lt;/p&gt;

&lt;p&gt;Initially, I considered implementing filtering logic in the MCP server (exact match, partial match, fuzzy search, etc.), and returning a narrowed-down list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7e1s3hf21vt6xpxu6yvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7e1s3hf21vt6xpxu6yvi.png" alt="architecture2" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, since the list of available languages is about 700 entries in a JSON object, I realized I could always return the entire list and let the AI editor process it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxogoarffxx5sm0hduumb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxogoarffxx5sm0hduumb.png" alt="architectuire3" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In practice, this worked very well, and I was able to greatly reduce implementation complexity on the MCP server side.&lt;/p&gt;

&lt;p&gt;The key takeaway:&lt;br&gt;
    • Let the AI client handle tasks where possible&lt;br&gt;
    • Keep the MCP server simple&lt;br&gt;
    • If performance or accuracy drops due to too much data, consider adding filtering later&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;h3&gt;
  
  
  The importance of revisiting responsibilities
&lt;/h3&gt;

&lt;p&gt;Initially, I worked with Cursor to create a design.md. At that stage, I separated responsibilities into two layers:&lt;br&gt;
    • A layer for MCP protocol implementation and communication with AI editors&lt;br&gt;
    • A layer for communication with DevDocs and data processing&lt;/p&gt;

&lt;p&gt;However, as I added more instructions like for example, “please handle X process”, the original files kept expanding, and some classes ended up bloated with too many responsibilities.&lt;/p&gt;

&lt;p&gt;Problems encountered:&lt;br&gt;
    • Overlapping configurations between config.yaml and .env&lt;br&gt;
    • Duplicate logic scattered across multiple files (e.g., “extract 20 items from a list” implemented in several places)&lt;/p&gt;

&lt;p&gt;Eventually, I had to refactor, restructuring into controller, application, and repository layers with clearer responsibilities.&lt;/p&gt;

&lt;p&gt;Lesson: AI editors tend to keep adding to existing files rather than creating new ones. You need to explicitly direct them to rethink responsibilities and maintain architecture consistency.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;h3&gt;
  
  
  Development caveats
&lt;/h3&gt;

&lt;p&gt;MCP communicates with AI editors over stdin/stdout.&lt;br&gt;
Since stdin/stdout only naturally works between parent and child processes, Cursor cannot directly connect to an already running MCP server (e.g., started manually via docker-compose up).&lt;/p&gt;

&lt;p&gt;To handle this, I:&lt;br&gt;
    • Prepared a shell script that runs docker-compose, and pointed the MCP configuration to that script&lt;br&gt;
    • The script stops any already running container before starting a new one&lt;/p&gt;

&lt;p&gt;This prevented errors when Cursor connected, even if the user had already started the container manually.&lt;/p&gt;

&lt;p&gt;At first, I was confused (“Why is there no log when Cursor sends requests…?”), but once I understood stdin/stdout limitations, it made sense. Knowing this upfront saves a lot of wasted time.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Note
&lt;/h2&gt;

&lt;p&gt;If you’re interested, please give it a try!&lt;br&gt;
And if something doesn’t work or you find bugs, I’d appreciate it if you could open an issue 🙌&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/katsulau/devdocs-mcp" rel="noopener noreferrer"&gt;https://github.com/katsulau/devdocs-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;*1 &lt;a href="https://cursor.com/en-US/learn" rel="noopener noreferrer"&gt;cursor learn&lt;/a&gt; also mentions that outputs are not always deterministic.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>opensource</category>
      <category>cursor</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
