概要
様々な言語の公式ドキュメントを集約している DevDocs というOSSがあります。そのdevdocsを、AIエディター(ClaudeやCursorなど)から参照できるMCP(Model Context Protocol)サーバーを構築しました。devdocs、MCPサーバーともにローカル環境で完結します。dockerさえ入っていれば、誰でも手元で動かせます。
構成は下記になります。
作った理由
新しい言語を効率よく学びたい
公式ドキュメントをリソース元として提示できれば、信頼性のある知識として習得できます。
実装の妥当性を判断できる
ClaudeやCursorの回答だけに依存せず、公式リファレンスを参照しながらコーディングすることで、より根拠を持った判断が可能になります。
実装の選択肢を広げられる
一次情報であるリファレンスを確認することで、より多様な書き方や設計の選択肢を検討できます。ClaudeやCursorを利用していると、自分で調べて実装を行うという手間は減りましたが、その反面、自分で考えて設計を行い、適切な実装を選択するという機会も減ったように思います。
- 実装をclaudeやcursorを利用して進める
- 生成されたコードについてMCP経由で確認する
- devdocsのリンク付きで回答をもらう
- 必要があれば、リンクを開いてリファレンスを読む
といったように、シームレスに公式リファレンスを参照しながら、どういったユースケースで使えるものなのか、補足的に知識を補っていくことで、自身の設計、実装の幅を広げることができると考えています。そのようなUXを構築したいと思ったのも一つの理由です。
使い方
リポジトリはこちらです:
セットアップ手順は以下の通りです。
git clone https://github.com/katsulau/devdocs-mcp.git
cd devdocs-mcp
cp .env.example .env
# DevDocsとMCPサーバーを起動
# 注意: devdocsのdocker imageが大きいので、初回起動では10分以上かかる可能性があります
docker-compose up -d
# 起動確認
docker-compose ps
起動後、http://localhost:9292 にアクセスすると、DevDocsを確認できます。
AIエディターでの設定手順
claude、cursorで使う場合の設定手順は、それぞれ下記となります。
slash commandをインストール
slash commandをインストールします。利用したいプロジェクト配下でnpxコマンドを打つと、slash command用のファイルが作成されます。
やり方は下記になります。
// 日本語のslash command
npx devdocs-mcp-commands@latest --preset claude --lang ja
デモ
cursorで利用した時の画面になります。
MCP構築で学んだこと
今回、MCPを構築するにあたり、Cursorを利用しました。その中で学んだことを記しておきます。
1. インプットの推論余地をなくすと精度が上がる
cursor やclaudeといったAI editorでは、全く同じ入力を行ったとしても必ず同じ出力をするわけではありません*1。最初は「入力文から適切な言語を自動判定して検索する」方式を考えましたが、以下の問題がありました。
- 検索対象となる言語抽出に時間がかかる
- ユーザーが入力した検索文によっては対象が見つからない
そこで、スラッシュコマンド内で直接パラメータを指定する方式に切り替えたところ、処理の安定性と回答精度が大幅に改善しました。
2. アウトプットの生成には、AI Editor側に委ねられるところは任せる
ユーザーがdevdocsに探したい言語が存在するかどうかを確認できる、エンドポイントも用意しています。
これを作る際、最初の段階では、MCPサーバーからAI に返すレスポンスとして、ユーザーの入力したキーワードを元に、候補となる言語の絞り込みを行う処理を書く方針で考えていました。図にすると下記の形です。
候補言語の絞り込み処理としては、AIエディターから受けとったinputを元に、例えば完全一致、部分一致、ファジー検索(inputの文字列がtypoしている場合の検索)などを考慮して、ヒットしたもののリストを返す、といった実装を考えていました。
最初はその実装を進めていたのですが、そもそも言語の選択肢のリストは、json型のオブジェクトで700件ほどです。データ量的にMCPサーバー側で絞らなくても、常に全てのresponseを返し、AIエディター側でその情報を元に回答を作成してもらえるかもしれない、と考えました。
実際にその形で動かしてみたところ、かなり良い形で回答が得られるとわかったので、大幅にMCPサーバー側の実装を減らすことができました。
もちろん、MCPサーバーのレスポンスとして、どの程度の量の情報を返すかによって、AIエディターがユーザーに返すレスポンスの質も変わる可能性があります。
ここは検証をしながら、一定委ねられるところはAI Client側に処理を任せる形にすれば、MCP側の実装がシンプルになるのでいいと思います。
そしてMCPサーバーのレスポンスのデータ量が大量になり、AIエディター側での回答精度が落ちているようであれば、そのタイミングでMCPサーバー側での絞り込み処理を追加検討する、みたいなプロセスが良いのではと思いました。
3. 責務の見直しの重要性
最初に、要件について cursor と壁打ちを行いながら design.md を作成しました。
その段階では
• MCPプロトコルの実装や AIエディターとの通信を担うレイヤー
• DevDocsとの通信や、取得データの加工処理を担うレイヤー
の2つを切る形で整理しました。
この設計を元にソースコードを生成しましたが、その後「〜な処理をしてほしい」といった粒度の指示を追加していくと、最初に作成されたファイルに次々と処理が書き足され、結果として1つのクラスが大きな責務を抱えて肥大化していく状態になってしまいました。
また、実装スピードは非常に早い反面、cursor に任せることで次のような問題が発生しました。
• config.yaml と .env が共存し、設定が重複する
• 同じ処理が複数ファイルに散在する(例: 「リストから20件抽出」といった処理があちこちに重複する)
最終的には、controller・application・repository といったレイヤーを切り直し、それぞれの責務を明確にした上で、少しずつリファクタリングを行う必要がありました。
使っていて感じたのは、「既存のレイヤーに沿って実装を追加していく」傾向が強く、責務を見直して新しいファイルを切り出すような動きは、こちらから明示的に指示しない限り起こらない という点です。
そのため、指示を出しながらも「全体のアーキテクチャや責務の整合性が保たれているか」を定期的に確認することが大切だと感じました。
4. 開発での注意点
MCPとAIは 標準入出力(stdin/stdout) で通信します。
stdin/stdout は「親プロセスと子プロセスの間」でしか自然に確立できない仕組みで、既に動いている別プロセスに後から差し込むことはできません。
そのため、ユーザーが docker-compose で立てたMCPサーバーに対して、Cursorが直接接続することはできません。CursorがMCPに接続する際は、Cursor自身が docker compose run を実行してコンテナを起動する必要があります。
今回のMCPサーバーでは、この仕組みに対応するため、以下のようにしました。
• docker compose を実行する sh ファイルを用意して、MCP設定にそのパスを指定
• shファイルの中で「既にコンテナが動いていたら一度止めてから起動し直す」処理を記述
これにより、ユーザーが手動で docker compose up していても、Cursorから接続したときにエラーにならないようにしています。
開発中は自分で docker compose up してビルド確認することが多いので、この点を知らずに最初は「Cursorからリクエストしてもログが出ない…?」となってしまい、デバッグするまでにやや時間がかかってしまいました。知っておくと無駄に時間を食わずに済むポイントなのではと思います。
最後に
興味ある方はぜひ使ってみてください!!またうまく動かない、バグがあるといった場合はissueにあげていただけると助かります!
*1 cursor learnにも「毎回同じ結果が得られるわけではない」という記述があります。cursor learnではAIモデルについて簡潔にまとまってるのでサクッと学ぶのにおすすめです。
- この記事は Zennにも掲載しています