Oct 14, 202615 分 READ

Claude Code で Language Server Protocol (LSP) を使うべき理由

Roger Winter

Content Marketing Manager

Claude Code のようなエージェント型コーディングツールは、コードベース全体にわたってコードの記述、リファクタリング、デバッグを行えます。しかしデフォルトでは、grep と同じようにコードをプレーンテキストとして読み取ります。

Language Server Protocol (LSP) はこれを変えます。LSP は IDE が使うのと同じコードインテリジェンスの層であり、エージェントに組み込むことで、文字列の一致ではなく意味に基づいてコードを読めるようになります。コードベースが大きくなるほど、シンボルを読み違えたときの代償は大きくなり、この構造的な視点の価値も高まります。

Claude Code の LSP サポートは、現在では標準で組み込まれています。この記事では、LSP とは何か、なぜ役立つのか、そして数分で試す方法を紹介します。

Language Server Protocol (LSP) とは?

Language Server Protocol (LSP) の仕組みを示す図

Language Server Protocol (LSP) は、Microsoft が Visual Studio Code のために作成し、2016 年に公開した JSON-RPC ベースの標準です。LSP を使うと、あらゆるエディターが言語サーバーと通信できます。言語サーバーは、定義へのジャンプ、参照の検索、ホバーでの型表示、診断、安全なリネームといったコードインテリジェンスを提供します。1 つのサーバーが多くのエディターで動作するため、エディターごとに、言語ごとの機能を作り直す必要がありません。

LSP 以前は、すべてのエディターがすべての言語に対して独自の統合を必要としていました。エディターが 10 個、言語が 10 個あれば、100 通りの実装が必要だったのです。LSP では、各言語が 1 つのサーバーを、各エディターが 1 つのクライアントを提供し、共通のプロトコルでやり取りします。Go の言語サーバーを一度書けば、Neovim、VS Code、Emacs、Claude Code のすべてで使えます。

実際の処理を担うのは言語サーバーです。ソースコードを解析して、完全なシンボルテーブルを持つ構文木を作るため、テキスト検索ではなくコンパイラーと同じ方法で質問に答えます。関数がどこで定義されているかを尋ねれば、その名前をたまたま含むすべての行ではなく、本当の定義を 1 つだけ返します。

同じサーバーが、コードの変更に応じて型エラーや不足しているインポートを報告し、シンボルが使われているすべての場所を一覧にし、プロジェクト全体で安全にリネームします。これらは、定義へのジャンプ、参照の検索、そしてタイプミスの下に表示される赤い波線を支える操作です。

Claude Code で LSP を使う理由

LSP が Claude Code の役に立つのは、確率的なテキスト検索を、決定的でコンパイラー並みに正確な答えに置き換えるからです。実質的に、開発者が IDE で得ているのと同じ視点をエージェントに与えます。

LSP がない場合、エージェントはファイルを読み、テキスト検索を実行してコードベースを探索します。それでも機能はしますが、推測に頼ることになります。createUser を grep すると、エージェントは定義、呼び出し、コメント、さらに同じ名前の無関係なシンボルまで受け取り、どれがどれなのかを推論しなければなりません。推測のたびにトークンを消費し、誤った編集のリスクも生じます。

言語サーバーは、この推測をなくします。唯一の定義、正確な参照の一覧、任意の位置で解決された型を返します。

Claude Code が LSP を使って効率を高める仕組みを示す図

LSP はプロジェクトの型グラフをメモリ上に保持しているため、編集によって生じた型エラーや不足しているインポートを数ミリ秒以内に報告します。

大規模なプロジェクトでは、完全な型チェックに数十秒かかることもあり、変更のたびに繰り返すには遅すぎます。言語サーバーは同じシグナルを Claude に差分で提供するため、Claude は後のビルドで失敗するコードを出荷するのではなく、同じターンの中で破損を修正できます。

同じチェックは、存在しない API のハルシネーションも抑えます。存在しない関数の呼び出しは、もっともらしく見えるでたらめなコードとしてコンパイルされるのではなく、診断として表面化します。

Claude Code は、組み込みの LSP ツールとコードインテリジェンスプラグインを通じて言語サーバーに接続します。CI での本格的な型チェックやテスト実行の代わりにはなりませんが、明らかな失敗を、修正コストが最も低い段階で捉えます。

Claude Code で LSP をセットアップする

この例では TypeScript の LSP をセットアップしますが、同じ流れはどの言語サーバーにも応用できます。Claude Code は 11 の言語向けに公式プラグインを提供しており、Python、Go、Rust、Java、TypeScript もその中に含まれます。それぞれ、専用のバイナリが $PATH 上に必要です。現在のラインナップはプラグインマーケットプレイスで確認できます。

Claude Code での LSP のセットアップに必要なのは、3 つのコマンドだけです。言語サーバーをインストールし、対応するプラグインをインストールして、再読み込みします。このプラグインがラップしているのは typescript-language-server で、TypeScript 自身の tsserver を LSP 経由で公開するコミュニティ製のサーバーです。

まず、サーバーのバイナリをインストールして $PATH 上に置きます。

npm install -g typescript-language-server typescript

次に、公式のコードインテリジェンスプラグインを追加して再読み込みします。

/plugin install typescript-lsp@claude-plugins-official
/reload-plugins

コードインテリジェンスプラグインをインストールすると、Claude Code の組み込み LSP ツールが有効になります。バイナリが $PATH 上にあれば、TypeScript のプロジェクトを開くだけで、追加の設定なしに Claude が言語サーバーに接続されます。

独自のコードインテリジェンスプラグインを作成する

11 の公式プラグインは主要な言語をカバーしていますが、言語サーバーがある言語なら何でも接続できます。Claude Code は、プラグインのルートにある .lsp.json ファイル、または plugin.json の lspServers フィールドから LSP の設定を読み込みます。この設定では、サーバー名を、そのサーバーを起動するコマンドと、サーバーが扱うファイル拡張子に対応付けます。最小限の Go のエントリは次のようになります。

{
  "go": {
    "command": "gopls",
    "args": ["serve"],
    "extensionToLanguage": { ".go": "go" }
  }
}

必須フィールドは command と extensionToLanguage だけです。args、transport (デフォルトは stdio、または socket)、env、initializationOptions は任意です。command に $PATH 上の任意の LSP バイナリを指定し、扱う拡張子を列挙すれば、Claude はその言語でも同じ診断とナビゲーションを利用できます。完全なスキーマはプラグインリファレンスに記載されています。

LSP と grep の比較: 精度、トークン、そして使うべき場面

Claude Code が LSP から何を得られるのかを理解するには、LSP を使う Claude と grep を使う Claude のパフォーマンスを比べる必要があります。言語サーバーがない場合、Claude の組み込みのコード検索はプレーンテキスト (内容には grep、ファイル名には glob) なので、ナビゲーションに関するあらゆる質問が文字列の一致の問題になります。

LSP が grep に勝るかどうかは、主にコードベースの規模と複雑さによって決まります。大規模で階層が深いリポジトリや、複数の言語にまたがるリポジトリほど恩恵が大きく、小規模で整理されたプロジェクトでは差がほとんど出ないこともよくあります。両者は競合するものではありません。LSP は意味的な質問 (これはどこで定義されているか、何がこれを呼び出しているか、このリネームは安全か) に答えます。TODO コメントや設定の文字列のようなリテラルテキストには、引き続き grep が適したツールです。

大まかな判断基準は次のとおりです。

LSP を使うべき場合 grep で十分な場合
リポジトリが大規模、レガシー、または複数の言語にまたがる プロジェクトが小規模で、勝手がわかっている
タスクがリファクタリング、リネーム、呼び出し箇所の追跡 対象がリテラルの文字列やパターン
誤ったシンボルの編集を後で発見するコストが高い 簡単なテキスト一致で答えが出る

最もはっきりした利点は精度です。LSP はコンパイラーのシンボルグラフから答えるため、参照の正確な集合を返し、grep が拾ってしまう同名のメソッド、コメント、文字列リテラルを除外します。grep が返すのは、エージェントが読んでふるい分けなければならない余分を含んだ結果であり、大規模なリポジトリではそれがトークンのコストになります。これが実際にどれほど重要なのかを確かめるため、私たちは独自のテストを実施しました。

実際に検証: 2 つのモデルで LSP と grep を比較

grep と LSP を比較した Claude Code コードインテリジェンスのベンチマーク結果

vuejs/core (約 14.9 万行の TypeScript) を対象に Claude Code を実行し、同じ名前が多くの場所で使われているリアクティビティ関連のシンボルについて、すべての呼び出し箇所を見つけるよう依頼しました。モデルは Opus 4.8 と Sonnet 4.6 の両方を使いました。

LSP をネイティブに使うとは、LSP に本来の仕事をさせることです。Claude はデフォルトで grep を使うため、プロジェクトの CLAUDE.md に次の指示を 1 つ追加しました。

“For symbol navigation, prefer the LSP tool over grep; use grep only for literal text; and trust the language server's results rather than re-reading files to confirm them.”

指示の最後の部分 (ファイルを読み直して確認するのではなく、言語サーバーの結果を信頼する) は、補足ではなく手法の核心です。LSP の出力を信頼していなければ、Claude は findReferences を呼び出した後、すべてのファイルを開き直して二重チェックし、grep よりも多くのトークンを費やしてしまいます。信頼されていれば、言語サーバーは本来の役割を果たせます。

ベンチマークでは 2 つの結果が際立ちました。1 つ目は、一度も見落としがなかったのは LSP を使った構成だけだったことです。Opus はどちらのツールでも正解しましたが、Sonnet は grep だけの場合、他のパッケージにある trigger の参照を 2 つ見落とし (11 個中 9 個)、effect も少なく数えました (260 個中 249 個)。まさに、リネームを中途半端なまま終わらせてしまう、気づきにくい見落としです。言語サーバーを使った場合、Sonnet はすべてのタスクですべての参照を見つけました。

モデルの性能が低いほど、LSP の効果は大きくなります。LSP は、信頼性の下限を支える役割を果たします。

2 つ目は、ネイティブに使った場合、最も難しい作業では LSP のほうが安価でもあったことです。規模が大きくノイズの多い effect タスクでは、LSP は両方のモデルで grep より安く、260 個すべての呼び出し箇所を見つけました (Opus では $0.50 対 $0.62、Sonnet では $0.24 対 $0.29)。ツール出力も 2〜3 倍コンパクトでした。findReferences が正確な集合を返すのに対し、grep はふるい分けが必要な大量の結果を返すからです。

小さなタスクでは両者のコストはほぼ同じで、LSP の固定オーバーヘッドは、LSP を読み込むためのツール検索の呼び出し 1 回だけでした。6 つのタスク全体では、LSP は価格とトークン使用量の両方で上回り、grep の $2.02 に対して $1.88 でした。

モデルごとの内訳は次のとおりです。

Opus 4.8 での LSP と grep のベンチマーク

タスク (正解数) ツール 時間 コスト ツール出力のトークン数
trigger (11) grep 70 秒 $0.39 3,041
trigger (11) LSP 74 秒 $0.40 3,666
track (20) grep 40 秒 $0.25 2,868
track (20) LSP 56 秒 $0.32 2,970
effect (260) grep 131 秒 $0.62 12,764
effect (260) LSP 117 秒 $0.50 6,280
合計 grep 241 秒 $1.26 18,673
合計 LSP 247 秒 (+2%) $1.22 (-3%) 12,916 (-31%)

両方のモデルで同じ 3 つのタスクを実行したのは、モデルの性能によって言語サーバーの価値が変わるからです。Opus では、効果はトークンの削減に集中しています。一方 Sonnet では、すべての合計で LSP が上回りました。

Sonnet 4.6 での LSP と grep のベンチマーク

タスク (正解数) ツール 時間 コスト ツール出力のトークン数
trigger (11) grep* 100 秒 $0.23 2,577
trigger (11) LSP 97 秒 $0.28 1,733
track (20) grep 101 秒 $0.25 3,808
track (20) LSP 49 秒 $0.14 491
effect (260) grep** 158 秒 $0.29 6,212
effect (260) LSP 92 秒 $0.24 6,256
合計 grep 359 秒 $0.77 12,597
合計 LSP 238 秒 (-34%) $0.66 (-14%) 8,480 (-33%)

Sonnet での 2 回の grep の実行を除き、すべての実行ですべての参照を見つけました。*11 個中 9 個を検出、**260 個中 249 個を検出。

LSP のコスト: メモリ、コールドスタート、設定

LSP にもコストはあります。

  1. メモリ: 言語サーバーはプロジェクトのシンボルグラフをメモリ上に保持します。rust-analyzer は大規模なワークスペースで数 GB を使うことも珍しくなく、pyright のような重量級のサーバーも同様の傾向をたどります。
  2. コールドスタート: サーバーは最初の応答を返す前に、コードベースをインデックス化します。大規模なモノレポを新しくチェックアウトした直後には数分かかることもあり、その間のクエリは応答が遅いか、空の結果が返ります。
  3. 設定: 言語ごとに専用のサーバーバイナリが $PATH 上に必要です。また、サーバーはプロジェクトのルートを基準に動作するため、リポジトリのルートより 1 階層下にある TypeScript のワークスペースでは、サーバーがまったく起動しないことがあります。長時間のセッションでは、ブランチの切り替えやコード生成の後に情報が古くなり、再起動が必要になる場合があります。

小規模で整理されたコードベースなら、エージェントはほとんどのファイルを低コストで読めます。LSP が真価を発揮するのは、プロジェクトが大きくなってからです。

まとめ

LSP は、Claude のインナーループをコンパイラー並みに正確なロジックに基づかせることで、コード作成を効率化します。テキスト検索による推測で時間を無駄にする代わりに、エージェントはコードベースを構造的にたどり、正確な編集をはるかに速く行えます。

この基盤が整えば、エージェント型のワークフローに即時の検証を取り入れて、さらに先へ進めます。Claude Code と CircleCI の Chunk CLI を組み合わせれば、軽量で分離された microVM である Chunk サイドカーを、エージェントのすぐ隣で実行できます。

これにより、開発ループに強力な相乗効果が生まれます。

  • LSP は、最初から正確なコードを書くための構造的な理解を Claude に与えます。

  • Chunk サイドカーは、対象を絞ったマイクロビルドとユニットテストをその場で実行できる即時のサンドボックスを Claude に与えます。

テストが失敗すると、Claude は LSP によるコードベースの理解をすぐに活かしてログを読み、リアルタイムで自己修復します。両者を組み合わせることで、コードを書くことと検証することの間のギャップが埋まり、壊れたコミットを 1 つも生むリスクなく、エージェントを非常に速く動かせます。

今すぐ CircleCI の無料アカウントに登録して、Chunk CLI を試し、LSP を活用したエージェント型ワークフローを強化しましょう。