tree-sitter-analyzer
🚀 初めてのMCP開発:Tree-sitter AnalyzerでLLMのトークン制限問題を解決する
🌟 はじめに
LLMを使った開発支援ツールが普及する中、大きなコードファイルを扱う際のトークン制限問題に直面したことはありませんか?
私は最近、この問題を解決するために初めてのMCP(Model Context Protocol)サーバーを開発しました。本記事では、その開発背景と技術的な解決策について詳しく紹介します。
😰 解決したかった痛点
⚠️ 既存ツールの限界
開発を始めた当初、code-index-mcpという素晴らしいプロジェクトを発見しました。しかし、以下の課題がありました:
- ☕ Java対応の不足 - 当時のバージョンではJavaサポートが限定的
- 🔍 正規表現ベースの解析 - コード構造の理解に限界がある
- 📊 大規模ファイルへの対応不足 - LLMのトークン制限を根本的に解決できない
💡 実際の問題シナリオ
例えば、1000行を超えるJavaクラスファイルを分析したい場合:
// BigService.java (1000+ lines)
public class BigService {
// 数十のメソッド、フィールド、内部クラス...
public void updateCustomerName(String customerId, String newName) {
// この特定のメソッドだけを理解したい
// しかし、ファイル全体がトークン制限を超えてしまう
}
}
従来のアプローチでは、ファイル全体をLLMに送信する必要があり、トークン制限に引っかかってしまいます。
🛠️ Tree-sitter Analyzerの解決策
1. 🌳 Tree-sitterによる高精度解析
正規表現ではなく、Tree-sitterという構文解析ライブラリを採用しました。
# 正規表現ベース(従来)
method_pattern = r'public\s+\w+\s+(\w+)\s*\([^)]*\)\s*{'
# Tree-sitterベース(我々のアプローチ)
parser = tree_sitter.Parser()
parser.language = tree_sitter_java.language()
tree = parser.parse(source_code.encode())
Tree-sitterの利点:
- ✅ 構文を正確に理解 - コメント、文字列リテラル内のコードを誤認識しない
- 🌍 多言語対応 - Java、Python、JavaScript、TypeScript等を統一的に処理
- ⚡ 高速処理 - C言語で実装された高性能パーサー
2. 📋 段階的分析アプローチ
大規模ファイルに対して、以下の3段階アプローチを提案:
📏 ステップ1:ファイル規模の確認
uv run python -m tree_sitter_analyzer examples/BigService.java --advanced --output-format=text
出力例:
--- Advanced Analysis Results ---
"File: examples/BigService.java"
"Package: (default)"
"Lines: 1419"
"Classes: 1"
"Methods: 66"
"Fields: 9"
"Imports: 8"
"Annotations: 0"
📊 ステップ2:構造の把握
uv run python -m tree_sitter_analyzer examples/BigService.java --table=full
出力例:
...省略
## Public Methods
| Method | Signature | Vis | Lines | Cols | Cx | Doc |
|--------|-----------|-----|-------|------|----|----|
| innerMethod | ():void | + | 84-86 | 5-6 | 1 | - |
| nestedMethod | ():void | + | 91-93 | 5-6 | 1 | - |
| getValue | ():String | + | 108-110 | 5-6 | 1 | - |
| staticMethod | ():void [static] | + | 128-130 | 5-6 | 1 | - |
| finalMethod | ():void | + | 133-135 | 5-6 | 1 | - |
| doSomething | ():void | + | 138-141 | 5-6 | 1 | - |
| anotherMethod | ():void | + | 143-146 | 5-6 | 1 | - |
| genericMethod | (input:T):void | + | 149-151 | 5-6 | 1 | - |
| createList | (item:T):List<T> | + | 154-158 | 5-6 | 1 | - |
## Private Methods
| Method | Signature | Vis | Lines | Cols | Cx | Doc |
|--------|-----------|-----|-------|------|----|----|
| privateMethod | ():void | - | 123-125 | 5-6 | 1 | - |
🎯 ステップ3:必要な部分のみ抽出
uv run python -m tree_sitter_analyzer examples/BigService.java --partial-read --start-line 84 --end-line 86
3. 📈 トークン削減効果
実際の効果を数値で示すと:
シナリオ | 対象 | トークン数 | 削減率 |
---|---|---|---|
従来手法 | ファイル全体 | 約15,000 | - |
詳細解析 | Markdown + 対象コード | 約4,500 | 70% |
サマリー解析 | サマリー + 対象コード | 約2,800 | 81% |
🤖 MCP統合による使いやすさ
🔗 Claude Desktopとの連携
設定ファイル(claude_desktop_config.json
)に以下を追加するだけ:
{
"mcpServers": {
"tree-sitter-analyzer": {
"command": "uv",
"args": [
"run",
"--with",
"tree-sitter-analyzer[mcp]",
"python",
"-m",
"tree_sitter_analyzer.mcp.server"
]
}
}
}
🛠️ 提供するMCPツール
- 📊 analyze_code_scale - コードの規模と複雑度を分析
- 📋 analyze_code_structure - 詳細な構造テーブルを生成
- 🎯 read_code_partial - 特定行範囲を抽出
- 🔍 analyze_code_universal - 自動言語検出による汎用分析
💬 実際の使用例
Claude Desktopで以下のように質問するだけ:
"BigService.javaの全体的な複雑度とサイズを教えて"
→ analyze_code_scale
ツールが自動実行
"BigService.javaの構造を詳細なテーブルで表示して"
→ analyze_code_structure
ツールが自動実行
"BigService.javaの84-86行目を見せて"
→ read_code_partial
ツールが自動実行
⚙️ 技術的な工夫
1. 🔤 エンコーディング対応
多様なファイルエンコーディングに対応:
def read_file_safe(self, file_path: Path) -> tuple[str, str]:
"""安全なファイル読み込みとエンコーディング検出"""
with open(file_path, 'rb') as f:
raw_data = f.read()
detected = chardet.detect(raw_data)
encoding = detected['encoding'] or 'utf-8'
return raw_data.decode(encoding, errors='replace'), encoding
2. 🛡️ エラーハンドリング
堅牢なエラー処理により、解析失敗時も適切な情報を提供:
@handle_mcp_errors("analyze_code_scale")
async def _analyze_code_scale(self, arguments: dict[str, Any]) -> dict[str, Any]:
try:
return await self.universal_analyze_tool.execute(arguments)
except FileNotFoundError:
return {"error": "ファイルが見つかりません"}
except PermissionError:
return {"error": "ファイルアクセス権限がありません"}
3. 🔧 プラグインアーキテクチャ
言語ごとの特殊な処理を分離:
class JavaPlugin(LanguagePlugin):
def extract_methods(self, tree: Tree, source_code: str) -> list[CodeElement]:
"""Java特有のメソッド抽出ロジック"""
query = self.query_loader.get_query("java", "methods")
captures = query.captures(tree.root_node)
# Java特有の処理...
📚 開発で学んだこと
🤖 MCPの魅力
- 🔗 標準化されたプロトコル - AI assistantとの統合が簡単
- 🛡️ 型安全性 - Pydanticによるスキーマ定義
- ⚡ 非同期処理 - 大規模ファイルの処理に適している
🌳 Tree-sitterの威力
- 🌍 言語中立性 - 一度の実装で多言語対応
- 🎯 高精度 - 構文を正確に理解
- 🔧 拡張性 - 新しい言語の追加が容易
🎉 まとめ
Tree-sitter Analyzerは、LLMのトークン制限問題を根本的に解決するツールです:
- 81%のトークン削減 を実現
- Tree-sitterによる高精度解析
- MCPによるシームレスな統合
- 多言語対応(Java、Python、JavaScript、TypeScript等)
初めてのMCP開発でしたが、実際の開発現場の痛点を解決できる実用的なツールを作ることができました。
🔗 リンク
大規模コードベースでの開発効率化に興味がある方は、ぜひお試しください!