はじめに
第2章では、GitHubにIssueを作成し、Pull Request(PR)にコメントを追加するMCPサーバーを構築し、開発タスク自動化エージェントを実現しました。これにより、AIがIssueやPRを操作できるようになり、チームの効率が向上しました。今回は、この基盤を活用して、GitHubリポジトリのコミットデータを解析するコード分析エージェントを構築します。
この第3章では、コミット履歴を分析し、コード変更頻度、主要貢献者、変更パターンのインサイトを生成します。たとえば、AIが「特定のファイルが頻繁に変更されている」ことを検出したり、「特定の開発者が特定のモジュールに集中している」ことを指摘したりできます。コード例とステップごとのガイドで、コード分析AIの構築を体験しましょう。さあ、始めましょう!
コード分析エージェントとは?
コード分析エージェントは、GitHubリポジトリのコミットデータを解析し、開発プロセスに関するインサイトを提供するAIです。MCPサーバーを介して、以下のような機能を実現できます:
- 変更頻度分析:ファイルやディレクトリごとの変更頻度を計算。
- 貢献者分析:開発者ごとのコミット数や変更量を評価。
- パターン抽出:コード変更の傾向(例:バグ修正、機能追加)を特定。
ユースケース
- コード品質:頻繁に変更されるファイルから潜在的な問題を検出。
- チームパフォーマンス:貢献者のワークロードや専門領域を評価。
- リファクタリング支援:変更パターンからリファクタリングの優先順位を提案。
開発環境の準備
第2章の環境を基に、以下の準備を行います:
- Python 3.8以降、mcpライブラリ、requestsライブラリ、Claude Desktop:これまでと同じ。
- python-dotenv:環境変数の管理(既にインストール済み)。
- GitHubリポジトリ:分析用のコミットデータを含むリポジトリ。
GitHubのセットアップ
-
リポジトリ準備:
- 第2章のリポジトリ(例:
yourusername/mcp-test-repo
)を使用。 - 複数のコミットを追加(例:コード変更、ドキュメント更新、バグ修正)。
- 異なる開発者(または模擬ユーザー)でコミットを作成。
- ファイル構造を多様化(例:
src/main.py
、docs/README.md
)。
- 第2章のリポジトリ(例:
-
GitHubトークンの確認:
- 第2章のトークンを使用(スコープ:
repo
)。 - トークンがコミットデータへの読み取り権限を持つことを確認。
- 第2章のトークンを使用(スコープ:
-
環境変数:
第2章の.env
ファイルに以下を確認:GITHUB_TOKEN=your_token GITHUB_REPO=yourusername/mcp-test-repo
コード例:コード分析用MCPサーバー
以下のMCPサーバーは、GitHubリポジトリのコミットデータを取得し、変更頻度と貢献者を分析します。
from mcp import MCPServer
import os
from dotenv import load_dotenv
import requests
from datetime import datetime, timedelta
from collections import Counter
import statistics
class GitHubAnalysisServer(MCPServer):
def __init__(self, host, port, token, repo):
super().__init__(host, port)
self.token = token
self.repo = repo
self.base_url = "https://api.github.com"
self.headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github.v3+json"
}
self.register_resource("analyze_commits", self.analyze_commits)
def get_commits(self, limit=100):
try:
url = f"{self.base_url}/repos/{self.repo}/commits"
query = {"per_page": limit}
response = requests.get(url, headers=self.headers, params=query)
response.raise_for_status()
return response.json()
except Exception as e:
return {"status": "error", "message": str(e)}
def get_commit_diff(self, sha):
try:
url = f"{self.base_url}/repos/{self.repo}/commits/{sha}"
response = requests.get(url, headers=self.headers)
response.raise_for_status()
files = response.json().get("files", [])
return [{"filename": f["filename"], "changes": f["changes"]} for f in files]
except Exception as e:
return []
def analyze_commits(self, params):
try:
commits = self.get_commits(limit=params.get("limit", 100))
if isinstance(commits, dict) and "status" in commits:
return commits
author_counts = Counter()
file_changes = Counter()
timestamps = []
change_types = Counter()
for commit in commits:
author = commit["commit"]["author"]["name"]
ts = commit["commit"]["author"]["date"]
timestamp = datetime.fromisoformat(ts.replace("Z", "+00:00"))
message = commit["commit"]["message"].lower()
# 貢献者分析
author_counts[author] += 1
# ファイル変更分析
files = self.get_commit_diff(commit["sha"])
for file in files:
file_changes[file["filename"]] += file["changes"]
# コミット頻度
timestamps.append(timestamp)
# 変更タイプ(簡易)
if "fix" in message or "bug" in message:
change_types["bug_fix"] += 1
elif "add" in message or "feature" in message:
change_types["feature"] += 1
else:
change_types["other"] += 1
# 頻度分析
if timestamps:
time_diffs = [(timestamps[i] - timestamps[i+1]).total_seconds() / 3600
for i in range(len(timestamps)-1)]
avg_interval = statistics.mean(time_diffs) if time_diffs else 0
commits_per_day = len(timestamps) / ((datetime.now(tz=timestamps[0].tzinfo) - min(timestamps)).total_seconds() / 86400)
else:
avg_interval, commits_per_day = 0, 0
# トップファイル
top_files = [{"file": f, "changes": c} for f, c in file_changes.most_common(5)]
return {
"status": "success",
"analysis": {
"commit_count": len(commits),
"avg_commit_interval_hours": f"{avg_interval:.2f}",
"commits_per_day": f"{commits_per_day:.2f}",
"top_contributors": [{"author": a, "count": c} for a, c in author_counts.most_common(3)],
"top_files": top_files,
"change_types": [{"type": t, "count": c} for t, c in change_types.items()]
}
}
except Exception as e:
return {"status": "error", "message": str(e)}
if __name__ == "__main__":
load_dotenv()
server = GitHubAnalysisServer(
host="localhost",
port=8129,
token=os.getenv("GITHUB_TOKEN"),
repo=os.getenv("GITHUB_REPO")
)
print("GitHub分析MCPサーバーを起動中: http://localhost:8129")
server.start()
コードの説明
- get_commits:リポジトリのコミットを取得(最大100件)。
- get_commit_diff:コミットの変更ファイルと変更行数を取得。
-
analyze_commits:コミットデータを解析し、以下のインサイトを生成:
- コミット総数:取得したコミットの数。
- 平均コミット間隔:コミット間の平均時間(時間単位)。
- 1日あたりのコミット数:開発の活発さを評価。
- トップ貢献者:コミット数が多い上位3ユーザー。
- トップファイル:変更頻度の高いファイル(行数ベース)。
- 変更タイプ:コミットメッセージから「バグ修正」「機能追加」「その他」を推定。
- register_resource:コード分析をリソースとして登録。
前提条件
- リポジトリに複数のコミットと異なるファイルが存在。
- GitHubトークンに
repo
スコープが付与されている。 -
.env
ファイルに正しいGITHUB_TOKEN
とGITHUB_REPO
が設定済み。
サーバーのテスト
サーバーが正しく動作するか確認します:
-
サーバー起動:
python github_analysis_server.py
コンソールに「GitHub分析MCPサーバーを起動中: http://localhost:8129」と表示。
-
コード分析のテスト:
Pythonでリクエストを送信:import requests import json url = "http://localhost:8129" payload = { "jsonrpc": "2.0", "method": "analyze_commits", "params": {"limit": 50}, "id": 1 } response = requests.post(url, json=payload) print(json.dumps(response.json(), indent=2, ensure_ascii=False))
期待されるレスポンス:
{ "jsonrpc": "2.0", "result": { "status": "success", "analysis": { "commit_count": 50, "avg_commit_interval_hours": "3.20", "commits_per_day": "8.50", "top_contributors": [ {"author": "Your Name", "count": 30}, {"author": "Contributor1", "count": 15}, {"author": "Contributor2", "count": 5} ], "top_files": [ {"file": "src/main.py", "changes": 150}, {"file": "docs/README.md", "changes": 50} ], "change_types": [ {"type": "bug_fix", "count": 10}, {"type": "feature", "count": 20}, {"type": "other", "count": 20} ] } }, "id": 1 }
Claude Desktopとの接続
サーバーをClaude Desktopに接続します:
-
設定ファイルの編集:
Claude Desktopの設定ファイル(例:claude_desktop_config.json
)に以下を追加:{ "mcp_servers": [ { "name": "GitHubAnalysisServer", "url": "http://localhost:8129", "auth": "none" } ] }
-
Claudeでテスト:
Claude Desktopを起動し、プロンプトを入力:リポジトリのコミットデータを分析してください。
レスポンス例:
リポジトリ yourusername/mcp-test-repo のコミット分析: - コミット数:50 - 平均コミット間隔:3.20時間 - 1日あたりのコミット数:8.50 - トップ貢献者:Your Name(30コミット)、Contributor1(15コミット)、Contributor2(5コミット) - 頻繁に変更されたファイル:src/main.py(150行)、docs/README.md(50行) - 変更タイプ:機能追加(20)、バグ修正(10)、その他(20)
実装のコツと注意点
- データ品質:コミットが少ない場合、分析結果が制限される。十分なデータ(例:50件以上)を用意。
- レートリミティング:GitHub APIの制限(例:5000リクエスト/時間)に注意。
-
セキュリティ:本番環境では、
auth: none
を避け、トークン認証を導入。 - テスト:テスト用リポジトリを作成し、本番データに影響を与えない。
- 拡張性:大量のコミットを処理する場合、キャッシュ(例:Redis)やNLPライブラリ(例:spaCy)でメッセージ分析を強化。
試してみよう:挑戦課題
以下の機能を追加して、エージェントを強化してみてください:
- 特定のファイル(例:
src/main.py
)の変更履歴だけを分析するフィルター。 - コミットメッセージの詳細分析(例:キーワード頻度)。
- 分析結果をIssueとしてリポジトリに投稿するツール。
まとめと次のステップ
この第3章では、GitHubのコミットデータを活用してコード分析エージェントを構築しました。変更頻度や貢献者を分析することで、AIが開発プロセスのインサイトを提供し、コード品質向上を支援できるようになりました。
次の第4章では、GitHubのWebhookを活用してリアルタイム管理AIを構築します。たとえば、AIが新しいIssueやPRの作成をリアルタイムで検知し、通知や応答を生成します。リアルタイム管理AIに興味がある方は、ぜひお楽しみに!
役に立ったと思ったら、「いいね」や「ストック」をしていただけると嬉しいです!次の章でまたお会いしましょう!