Open1

tree-sitter-analyzer

aisheng.yuaisheng.yu

🚀 初めてのMCP開発:Tree-sitter AnalyzerでLLMのトークン制限問題を解決する

🌟 はじめに

LLMを使った開発支援ツールが普及する中、大きなコードファイルを扱う際のトークン制限問題に直面したことはありませんか?

私は最近、この問題を解決するために初めてのMCP(Model Context Protocol)サーバーを開発しました。本記事では、その開発背景と技術的な解決策について詳しく紹介します。

😰 解決したかった痛点

⚠️ 既存ツールの限界

開発を始めた当初、code-index-mcpという素晴らしいプロジェクトを発見しました。しかし、以下の課題がありました:

  1. ☕ Java対応の不足 - 当時のバージョンではJavaサポートが限定的
  2. 🔍 正規表現ベースの解析 - コード構造の理解に限界がある
  3. 📊 大規模ファイルへの対応不足 - 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ツール

  1. 📊 analyze_code_scale - コードの規模と複雑度を分析
  2. 📋 analyze_code_structure - 詳細な構造テーブルを生成
  3. 🎯 read_code_partial - 特定行範囲を抽出
  4. 🔍 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の魅力

  1. 🔗 標準化されたプロトコル - AI assistantとの統合が簡単
  2. 🛡️ 型安全性 - Pydanticによるスキーマ定義
  3. ⚡ 非同期処理 - 大規模ファイルの処理に適している

🌳 Tree-sitterの威力

  1. 🌍 言語中立性 - 一度の実装で多言語対応
  2. 🎯 高精度 - 構文を正確に理解
  3. 🔧 拡張性 - 新しい言語の追加が容易

🎉 まとめ

Tree-sitter Analyzerは、LLMのトークン制限問題を根本的に解決するツールです:

  • 81%のトークン削減 を実現
  • Tree-sitterによる高精度解析
  • MCPによるシームレスな統合
  • 多言語対応(Java、Python、JavaScript、TypeScript等)

初めてのMCP開発でしたが、実際の開発現場の痛点を解決できる実用的なツールを作ることができました。

🔗 リンク

大規模コードベースでの開発効率化に興味がある方は、ぜひお試しください!