GitHubでAIを強化する | 第1章：リポジトリデータの第一歩

## はじめに

**GitHub**は、コード管理、コラボレーション、プロジェクト管理をサポートする世界最大の開発プラットフォームです。リポジトリ、Issue、Pull Request（PR）、コミット履歴を管理し、強力なAPIとWebhookでデータの取得や自動化が可能です。一方、**Model Context Protocol（MCP）**は、AI（例：Claude）を外部データやツールと標準化された方法で接続するプロトコルです。このシリーズでは、GitHubとMCPを統合し、リポジトリデータを活用するエージェントAIを構築する方法を学びます。たとえば、AIがIssueを分析したり、コード変更のインサイトを提供したりできます。

第1章では、GitHubとMCPの基本を理解し、GitHubリポジトリからIssueやコミットデータを取得するシンプルなMCPサーバーを構築します。Claude Desktopと接続して、開発管理の第一歩を踏み出しましょう。

## GitHubとMCPの概要

### GitHubとは？
GitHubは、以下のような特徴を持つ開発プラットフォームです：
- **リポジトリ管理**：コード、ドキュメント、データセットを保存。
- **強力なAPI**：REST APIでIssue、PR、コミット、ユーザーデータにアクセス。
- **Webhookサポート**：リアルタイムイベント（Issue作成、PRマージなど）を検知。
- **多用途**：個人プロジェクトから大規模なオープンソース開発まで対応。

### MCPの役割
MCPは、AIにGitHubのデータを提供する橋渡し役です。MCPサーバーを通じて、AIは以下を行えます：
- **データ取得**：Issue、PR、コミット情報を取得。
- **アクション実行**：Issue作成、コメント追加、PRレビュー。
- **分析**：コード変更やチームのコラボレーションパターンを評価。

### なぜGitHubとMCPを組み合わせる？
この組み合わせは、以下のようなシナリオで強力です：
- 自動Issue管理：AIがバグ報告を分類し、適切なラベルを付ける。
- コード分析：AIがコミット履歴から改善点を提案。
- 通知最適化：AIが重要なイベント（例：PRマージ）をSlackやメールで通知。

## 開発環境の準備

MCPサーバーとGitHubを統合するには、以下の環境を準備します：
- **Python 3.8以降**：サーバー開発用。
- **mcpライブラリ**：JSON-RPC通信を処理（仮定のライブラリ、公式ドキュメントを参照）。
- **requestsライブラリ**：GitHub APIとの通信用。
- **Claude Desktop**：MCP対応のAIクライアント。
- **テキストエディタ**：VS Code推奨。

インストールコマンド：
```bash
pip install mcp jsonrpcserver requests python-dotenv
```

### GitHubのセットアップ
1. **GitHub Personal Access Tokenの作成**：
   - [GitHub Settings](https://github.com/settings/tokens)にアクセスし、トークンを生成。
   - スコープを選択：`repo`（リポジトリアクセス）、`admin:org`（オプション、組織管理用）。
   - トークンを保存（例：`ghp_xxxxxxxxxxxxxxxx`）。
2. **リポジトリ準備**：
   - テスト用リポジトリ（例：`mcp-test-repo`）を作成。
   - Issueをいくつか追加（例：「バグ修正」「新機能提案」）。
   - コミットをいくつか作成（例：「初回コミット」「ドキュメント更新」）。
   - リポジトリ名とオーナー名（例：`yourusername/mcp-test-repo`）を記録。
3. **環境変数の設定**：
   `.env`ファイルに以下を追加：
   ```bash
   GITHUB_TOKEN=your_token
   GITHUB_REPO=yourusername/mcp-test-repo
   ```

## コード例：GitHub用MCPサーバー

```python
from mcp import MCPServer
import os
from dotenv import load_dotenv
import requests

class GitHubServer(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("get_issues", self.get_issues)
        self.register_resource("get_commits", self.get_commits)

    def get_issues(self, params):
        try:
            url = f"{self.base_url}/repos/{self.repo}/issues"
            query = {"state": params.get("state", "open"), "per_page": params.get("limit", 10)}
            response = requests.get(url, headers=self.headers, params=query)
            response.raise_for_status()
            issues = response.json()
            issue_list = [
                {
                    "title": issue["title"],
                    "number": issue["number"],
                    "state": issue["state"],
                    "creator": issue["user"]["login"],
                    "created_at": issue["created_at"]
                }
                for issue in issues
            ]
            return {"status": "success", "issues": issue_list}
        except Exception as e:
            return {"status": "error", "message": str(e)}

    def get_commits(self, params):
        try:
            url = f"{self.base_url}/repos/{self.repo}/commits"
            query = {"per_page": params.get("limit", 10)}
            response = requests.get(url, headers=self.headers, params=query)
            response.raise_for_status()
            commits = response.json()
            commit_list = [
                {
                    "sha": commit["sha"],
                    "message": commit["commit"]["message"],
                    "author": commit["commit"]["author"]["name"],
                    "date": commit["commit"]["author"]["date"]
                }
                for commit in commits
            ]
            return {"status": "success", "commits": commit_list}
        except Exception as e:
            return {"status": "error", "message": str(e)}

if __name__ == "__main__":
    load_dotenv()
    server = GitHubServer(
        host="localhost",
        port=8127,
        token=os.getenv("GITHUB_TOKEN"),
        repo=os.getenv("GITHUB_REPO")
    )
    print("GitHub MCPサーバーを起動中: http://localhost:8127")
    server.start()
```

### コードの説明
- **requests**：GitHub APIにGETリクエストを送信し、Issueやコミットを取得。
- **get_issues**：リポジトリのIssueを取得（タイトル、番号、状態、作成者、作成日）。
- **get_commits**：リポジトリのコミットを取得（SHA、メッセージ、著者、日付）。
- **register_resource**：Issueとコミット取得をリソースとして登録。
- **start()**：サーバーを起動。

### 前提条件
- GitHub Personal Access Tokenが生成済み。
- テスト用リポジトリにIssueとコミットが存在。
- `.env`ファイルに正しい`GITHUB_TOKEN`と`GITHUB_REPO`が設定済み。
- トークンに`repo`スコープが付与されている。

## サーバーのテスト

サーバーが正しく動作するか確認します：

1. **サーバー起動**：
   ```bash
   python github_server.py
   ```
   コンソールに「GitHub MCPサーバーを起動中: http://localhost:8127」と表示。

2. **Issue取得のテスト**：
   Pythonでリクエストを送信：
   ```python
   import requests
   import json

   url = "http://localhost:8127"
   payload = {
       "jsonrpc": "2.0",
       "method": "get_issues",
       "params": {"state": "open", "limit": 5},
       "id": 1
   }
   response = requests.post(url, json=payload)
   print(json.dumps(response.json(), indent=2, ensure_ascii=False))
   ```
   期待されるレスポンス：
   ```json
   {
     "jsonrpc": "2.0",
     "result": {
       "status": "success",
       "issues": [
         {
           "title": "バグ修正",
           "number": 1,
           "state": "open",
           "creator": "yourusername",
           "created_at": "2025-04-22T10:00:00Z"
         },
         {
           "title": "新機能提案",
           "number": 2,
           "state": "open",
           "creator": "yourusername",
           "created_at": "2025-04-22T11:00:00Z"
         }
       ]
     },
     "id": 1
   }
   ```

3. **コミット取得のテスト**：
   ```python
   payload = {
       "jsonrpc": "2.0",
       "method": "get_commits",
       "params": {"limit": 3},
       "id": 2
   }
   response = requests.post(url, json=payload)
   print(json.dumps(response.json(), indent=2, ensure_ascii=False))
   ```
   期待されるレスポンス：
   ```json
   {
     "jsonrpc": "2.0",
     "result": {
       "status": "success",
       "commits": [
         {
           "sha": "abc123",
           "message": "初回コミット",
           "author": "Your Name",
           "date": "2025-04-22T09:00:00Z"
         },
         {
           "sha": "def456",
           "message": "ドキュメント更新",
           "author": "Your Name",
           "date": "2025-04-22T10:00:00Z"
         }
       ]
     },
     "id": 2
   }
   ```

## Claude Desktopとの接続

サーバーをClaude Desktopに接続します：

1. **設定ファイルの編集**：
   Claude Desktopの設定ファイル（例：`claude_desktop_config.json`）に以下を追加：
   ```json
   {
     "mcp_servers": [
       {
         "name": "GitHubServer",
         "url": "http://localhost:8127",
         "auth": "none"
       }
     ]
   }
   ```

2. **Claudeでテスト**：
   Claude Desktopを起動し、プロンプトを入力：
   ```
   リポジトリのオープンIssueを教えてください。
   ```
   レスポンス例：
   ```
   リポジトリ yourusername/mcp-test-repo のオープンIssue：
   - #1: バグ修正（作成者：yourusername、2025-04-22）
   - #2: 新機能提案（作成者：yourusername、2025-04-22）
   ```

   別のプロンプト：
   ```
   最新のコミットを教えてください。
   ```
   レスポンス例：
   ```
   リポジトリ yourusername/mcp-test-repo の最新コミット：
   - SHA: def456、メッセージ：ドキュメント更新、著者：Your Name、2025-04-22
   ```

## 次のステップ

このMCPサーバーは、GitHubとAIの連携の基礎です。次の第2章では、GitHubにIssueを作成したり、Pull Requestにコメントしたりする機能を実装し、開発タスクを自動化するエージェントを構築します。たとえば、AIが特定のキーワードに基づいてIssueにラベルを付けることができます。

## まとめ

この第1章では、GitHubとMCPの基本を学び、シンプルなMCPサーバーを構築してIssueとコミットデータを取得しました。GitHubの強力なAPIとMCPの柔軟性を組み合わせることで、AIを開発プロセスの強力なアシスタントに変えられます。次章以降では、Issue管理、コード分析、リアルタイム管理へと進化させます。

---

役に立ったと思ったら、「いいね」や「ストック」をしていただけると嬉しいです！次の章でまたお会いしましょう！