Claude Code 向け Language Server Protocol (LSP) プラグインの作り方
Content Marketing Manager
言語サーバーは、エディターに構造化されたリアルタイムのフィードバックを提供します。診断、ホバードキュメント、オートコンプリートなど、本来なら後になって表面化するはずのガイダンスです。開発者が日常的に使う多くの言語やツールにはすでに言語サーバーが存在しますが、Claude Code はそのフィードバックを自動的には受け取りません。
Claude Code はほとんどの場合、構文的に正しいコードを書きます。それでも、言語サーバーなら編集時に示してくれるガイダンスを見逃すことがあります。ベストプラクティスの推奨、非推奨の警告、そして本来なら後になるまで現れないエラーなどです。
この記事では、既存の言語サーバーを Claude Code プラグインとしてラップする方法を紹介します。例として CircleCI のオープンソース YAML 言語サーバーを使い、他の言語サーバーにも再利用できる構成要素を順に解説します。
LSP 向けのカスタム Claude Code プラグインを作成する
作業対象の言語サーバーがすでに存在するなら、その LSP を Claude Code プラグインとしてラップすることで、同じシグナルを Claude の編集ループの中に直接渡せます。
私たちは公式の CircleCI 言語サーバーを使い、CircleCI の設定ファイルでこれを実現しました。CircleCI は VS Code 拡張機能を支える言語サーバーをオープンソースで公開しています。そのため、独自の検証ロジックを作る代わりに、この既存のサーバーをプラグインとしてラップしました。
このプラグインによって、Claude は VS Code で .circleci/config.yml を編集する人間が目にするのと同じ診断とホバーガイダンスを受け取れるようになりました。
以下では、プラグインの作り方を順に説明し、何の支援もなく編集した場合と比べて Claude の出力がどれだけ改善するかをベンチマークします。
言語サーバーと Claude Code の組み合わせが初めての方は、この実装に入る前に、一般的なケースを扱った前回の記事「Claude Code で Language Server Protocol (LSP) を使うべき理由」をご覧ください。
注: このプラグインはサンプルであり、CircleCI が公式にサポートする製品ではありません。ラップしている CircleCI YAML 言語サーバーはオープンソースで CircleCI がメンテナンスしていますが、ここで紹介する Claude Code プラグインは検証とアプローチの紹介を目的に作成したものです。公式サポートはなく、いかなる保証もありません。また、Claude Code や言語サーバーの進化に伴って動作しなくなる可能性があります。ご利用はご自身の判断と責任でお願いします。
Claude Code 向け CircleCI LSP プラグインの仕組み
CircleCI 設定ファイル用の LSP プラグインは、CircleCI のオープンソース YAML 言語サーバーをラップし、その対象を .circleci/config.yml に限定します。Claude がこのファイルを編集すると、言語サーバーから設定の診断とベストプラクティスのガイダンスを受け取ります。
Claude Code は、コードインテリジェンスプラグインを通じて言語サーバーに接続します。このサンプルプラグインは、CircleCI の設定ファイルについて Claude Code を CircleCI 言語サーバーに接続します。後述のベンチマークでは、言語サーバーのガイダンスにアクセスできたことで、Claude は store_test_results の追加や Orb を最新バージョンに保つといった改善を行いました。
ランチャーは、信頼できる言語サーバーのバイナリをマシンに用意する bash スクリプトです。バイナリをリポジトリに同梱することはありません。初回実行時に CircleCI の GitHub Releases から公式のビルド済みサーバーをダウンロードし、固定された SHA-256 と照合して検証したうえでキャッシュします。一致しない場合は実行を中止します。
スコーピングプロキシが一番の工夫どころです。Claude Code はファイル拡張子だけで言語サーバーを振り分けるため、.yml/.yaml を割り当てると、すべての YAML ファイルが CircleCI 専用のサーバーに送られてしまいます。プロキシは JSON-RPC ストリームの中に入り、.circleci/config.yml だけをサーバーに転送します。docker-compose.yml や Helm チャートには手を触れません。
スキーマホバーは、CircleCI の設定に関するインラインドキュメントを提供します。Claude が設定キーについて情報を求めると、プロキシは CircleCI の固定された設定スキーマからそのキーを探し、該当する説明を返します。
3 つのコマンドでサンプルプラグインを実行する
サンプルはご自身でも実行できます。Claude Code のセッションで、次の 3 つのコマンドを使います。
/plugin marketplace add CIRCLECI-GWP/circleci-yaml-lsp-for-claude
/plugin install circleci-yaml-lsp@circleci-lsp
/reload-plugins
プラグインは GitHub の CIRCLECI-GWP/circleci-yaml-lsp-for-claude で公開しています。
1 つ知っておきたい点があります。この言語サーバーはプッシュ型のため、診断が表示されるのは Claude が設定を編集したときで、読み込んだだけでは表示されません。Claude に .circleci/config.yml の変更を依頼すると、エラー、警告、ヒントがループの中に現れます。ヒントは、CircleCI 自身による編集時の使い方のガイダンスです。
設定でプライベート Orb やコンテキストを使っている場合は、サーバーがそれらを解決できるように CIRCLECI_YAML_LSP_TOKEN を設定してください。設定しなくても、パブリック Orb とスキーマは引き続き機能します。
このプラグインは CircleCI の公式製品ではありません。ラップしている言語サーバーは、公式の circleci-yaml-language-server です。
Claude Code LSP プラグインをゼロから作成する
ここから先の内容のほとんどは、このプラグインに限らず、あらゆる Claude Code LSP プラグインに使えるレシピです。独自の言語サーバーをラップしたい場合も、最初の 3 つのステップは同じで、最後のステップだけが CircleCI のサーバーに固有です。
プラグインは MIT ライセンスのオープンソースで、ドキュメントも整備されているため、以下のスニペットは各アイデアの骨格だけに絞っています。完全なファイルは GitHub にあり、内部構造は docs/DESIGN.md で解説しています。以下のリンクは実際のコードを指しています。
言語サーバーを接続する
Claude Code LSP プラグインは plugin.json で構成され、その lspServers フィールドが .lsp.json を指します。.lsp.json では、サーバー名を command と extensionToLanguage テーブルに対応付けます。完全なスキーマはプラグインリファレンスに記載されています。私たちの設定は次のとおりです。
{
"circleci-yaml": {
"command": "${CLAUDE_PLUGIN_ROOT}/bin/circleci-yaml-lsp",
"args": [],
"transport": "stdio",
"extensionToLanguage": { ".yml": "yaml", ".yaml": "yaml" },
"startupTimeout": 180000
}
}
${CLAUDE_PLUGIN_ROOT} はインストールされたコピーの場所に解決されます。180 秒の startupTimeout は、初回実行時のバイナリのダウンロードに必要な時間をカバーするためのものです。
バイナリを安全にマシンへ用意する
言語サーバーがユーザーの PATH 上にまだない場合、プラグインがそれを取得する必要があります。そして、バイナリをダウンロードして実行するものは、正しいバイナリを取得したことを証明すべきです。
私たちのランチャー bin/circleci-yaml-lsp は、アトミックかつフェイルクローズドなダウンロードを行います。まずファイルのバイトサイズを、次にスクリプトに埋め込まれた固定値と SHA-256 を照合し、両方を通過した場合にのみ、バージョンごとのキャッシュに移動します。sha256 ツールがない場合は、検証されていないバイナリを実行するのではなく、実行自体を拒否します。このパターンは、リリースバイナリとして配布されるあらゆるサーバーにそのまま再利用できます。
サーバーを対応するファイルだけに限定する
ここが最も再利用しやすいアイデアであり、唯一の本当に難しい部分です。Claude Code はファイル拡張子だけで言語サーバーを振り分け、パスや glob によるフィルターはありません。そして多くのドメイン固有のサーバーは、受け取ったドキュメントはすべて自分が検証すべきものだと想定しています。CircleCI のサーバーもそうです。
対象を限定しないままだと、無関係な YAML に対して誤った診断がモデルに渡されてしまい、LSP がまったくない状態よりも悪くなります。解決策は、小さな Node の stdio プロキシ bin/lsp-proxy.mjs です。このプロキシは JSON-RPC ストリームの中に入り、次の正規表現に一致しないファイルについて、ドキュメント同期の通知を破棄します。
/(^|\/)\.circleci\/([^/]*_)?config\.ya?ml$/i
この正規表現は config.yml、config.yaml、<prefix>_config.yml を通し、test-suites.yml、サブディレクトリ内のファイル、無関係な YAML を除外します。プロキシはストリームの残りをそのまま転送し、対象外の同期通知と診断だけを破棄します。
同じアプローチは、広いファイル拡張子に対応しているものの、実際にはより限られたファイルだけを受け取るべき、設定ファイルや方言に特化した他の言語サーバーにも使えます。
行儀の良いサーバーであれば、この 3 つのステップで十分です。
CircleCI LSP に固有の最後のステップが、ホバープロキシです。このプロキシは、CircleCI の設定スキーマから生成したテーブルをもとに、ホバーに自ら応答します (bin/lsp-hover.mjs)。CircleCI がスキーマを進化させるにつれて古くなる可能性があるのはこのテーブルだけなので、最新の状態に保つための CircleCI パイプラインを作成しました。
CircleCI でプラグインを最新の状態に保つ
ホバーテーブルは CircleCI のスキーマの特定バージョンから生成されており、ランチャーは特定のサーバーバイナリを SHA-256 で固定しています。CircleCI が言語サーバーの新しいリリースを出したら、どちらも更新する必要があります。
サンプルプロジェクトでは、こうしたリリースを手作業で追跡する代わりに、プロジェクト自身の CircleCI パイプラインで同期を保っています。パイプラインの全体は、プロジェクトの .circleci/config.yml ファイルで確認できます。
この設定では、プロジェクトの通常の CI と、アップストリームの更新を扱うメンテナンス用ワークフローを分けています。
workflows:
ci:
jobs:
- lint
- test
maintenance:
when: << pipeline.parameters.run-maintenance >>
jobs:
- upstream-update:
context:
- gh-bot
メンテナンスジョブが実行されると、アップストリームの言語サーバーの最新の安定版リリースと、ランチャーに固定されたバージョンを比較します。新しいリリースがない場合や、新しいリリースのバイナリとスキーマのアップロードがまだ終わっていない場合、ジョブは正常に終了し、後の実行で再試行します。
新しいリリースの準備ができていれば、ジョブは固定バージョンを更新し、4 つのプラットフォームすべてのバイナリについて SHA-256 とサイズの固定値を更新します。さらに、新しいスキーマからホバードキュメントのテーブルを再生成し、プラグインのバージョンを上げてテストを実行します。
すべてのテストに合格すると、差分を含むプルリクエストを作成します。main に自動でマージすることはありません。更新内容は人間がレビューしてマージします。
パイプライン、更新スクリプト、プロキシ、プラグインのソースを含む実装の全体は、circleci-yaml-lsp-for-claude リポジトリで確認できます。
プラグインのパフォーマンスをベンチマークする
プラグインによって Claude の出力が変わるかどうかを確かめるため、小規模で傾向を見るためのベンチマークを実施しました。
よくある 6 つの設定編集 (デプロイワークフロー、Orb コマンド、壊れた設定の修復、マトリックスジョブ、キャッシュ、Executor) を、ヘッドレスの Claude にそれぞれ 4 つの条件で試させました。プラグインなしのベースライン、単純なセルフレビューの指示、そして 2 つのプラグインありの条件です。
各実行は使い捨ての新しいプロジェクトで、Opus 4.8 と Sonnet 5 の両方を使って行い、安定したタスクは繰り返し実行して、合計 84 回となりました。出力されたすべての設定は、CircleCI のサーバーサイドのコンパイラーである circleci config validate で判定しました。どの条件でも、タスクの途中でこのコマンドを呼び出すことはできないようにしています。
結果 1: 正しさに差はない
84 個の出力設定はすべて、どの条件でも、両方のモデルでエラーなくコンパイルされました。このベンチマークでは、両モデルともプラグインの有無にかかわらず有効な CircleCI 設定を生成しました。
また、存在しない Orb コマンドの捏造、参照先のないジョブ、マトリックスの型エラーなど、タスクが誘発するように設計されていたコンパイラーレベルのミスも回避しました。
結果 2: プラグインによって Claude が設定を改善した
circleci config validate も、CI のグリーンな実行結果も、CircleCI LSP が編集時に出す警告やヒントについては何も教えてくれません。今回のタスクでは、そうした指摘が 2 つ現れました。テスト結果を CircleCI で表示するよう求める store_test_results のヒントと、古い Orb バージョンに関する通知です。
言語サーバーのガイダンスに従うよう指示された場合、Claude はこれらの改善を行いました。エラーだけを修正するよう指示された場合や、プラグインなしで実行した場合は、ほとんど改善を行いませんでした。
プラグインのガイダンスに従った場合、Claude はテストを実行する 16 個の設定のうち 5 個で store_test_results を追加しました。プラグインなしの実行では、追加したものは 1 つもありませんでした。また、プラグインのガイダンスに従った 6 個の Orb 設定すべてで Node Orb を最新バージョンに保ちましたが、プラグインなしの実行では 8 個中 7 個で古いバージョンのままでした。
どちらの場合もすべての設定がエラーなくコンパイルされたので、違いはコンパイラーレベルの正しさではありません。LSP は、コンパイラーが強制しないベストプラクティスのガイダンスを Claude が適用するのを助けたのです。
指示の内容は、プラグインと同じくらい重要でした。プラグインをインストールしても、エラーだけを修正するよう Claude に指示した場合は、追加の改善はありませんでした。改善が現れたのは、言語サーバーのガイダンスに従うよう Claude に指示したときです。
| 条件 | store_test_results を追加 |
Orb を最新に維持 |
|---|---|---|
| プラグインなし | テスト設定 24 個中 0 個 | Orb 設定 8 個中 1 個 |
| プラグインあり、エラーのみ修正するよう指示 | 16 個中 0 個 | 6 個中 2 個 |
| プラグインあり、ガイダンスに従うよう指示 | 16 個中 5 個 | 6 個中 6 個 |
Claude が能動的に改善した設定の数です。どの場合もすべての設定がエラーなくコンパイルされており、これらはコンパイラーが要求しなかったベストプラクティスの変更です。
プラグインの開発中にも、同じパターンが見られました。プラグイン自身の .circleci/config.yml はエラーなくコンパイルされましたが、言語サーバーは、テスト結果が CircleCI に表示されるよう store_test_results を追加することを提案しました。JUnit レポートと対応するステップを追加すると、設定の診断はゼロになりました。
これとは別に、lint ジョブの shellcheck が、ランチャーにある実際のシェルの問題を検出しました。
Claude はもともと正しい設定を書く力を持っていましたが、LSP のガイダンスにアクセスできたことで、CircleCI のベストプラクティスに従う可能性が高まりました。
独自の Claude Code LSP プラグインを作成する
このサンプルプラグインは、言語サーバーの編集時フィードバックを Claude Code に取り込む方法を示しています。今回のベンチマークでは、モデルはもともとコンパイラーレベルの正しさをうまく扱えていましたが、LSP へのアクセスによって、コンパイラーが強制しないベストプラクティスを適用しやすくなりました。
同じ実装パターンは CircleCI 以外にも使えます。多くのドメイン固有の言語サーバーでは、中核となる構成要素は、サーバーを検証済みの方法で起動する仕組み、サーバーを対応するファイルだけに限定する仕組み、そして Claude Code が直接サポートしていない連携固有の動作を補うプロキシです。
circleci-yaml-lsp-for-claude のサンプル全体はオープンソースなので、実装を確認したり、それを保守する CircleCI パイプラインをレビューしたり、同じパターンを別の言語サーバーに応用したりできます。
編集時のフィードバックがカバーするのは、開発ループの一部です。コードをプッシュした後に可能になる幅広い検証は CI が担います。テスト、結合チェック、セキュリティポリシー、そしてデリバリーワークフローの残りの部分です。
CircleCI の無料アカウントに登録して、今すぐビルドを始めましょう。