🐈

GitHub API + MCP + Claude:対話しながら1年間の実装内容を振り返る

に公開

はじめに

こちらはe-dash advent calendar 2025の 4 日目の記事です。

年末が近づくと、今年1年でどんなコードを書いて、どんな内容の対応をしたか振り返りたくなってきますよね。

マメに自分の対応をメモしていたり振り返ったりできる人は比較的楽に振り返りできると思うのですが、私のようなズボラな人間にはそんなことはできず、まとめて振り返ろうとして挫折するみたいになっていました…

ただ、最近は昔と違って、AI を活用すれば簡単に対応したPRの取得もできます。そのPRに対して、どんな内容だったか、何が良かったのか悪かったのかを対話しながら振り返ることもできるようになりました。

この記事では、その方法について考え、実践してみていることと、来年からはこういう工夫も入れてみたいなというアイデアについてまとめてきたので紹介します。

この記事のざっくりサマリ

  • GitHub APIを使ったPRの一括取得方法
  • GitHub の MCPサーバーを使ったAIとの対話的な分析
  • AIを活用した振り返りの方法
  • やってみて改善したほうがいいなと思った内容

振り返りの全体フロー

今回試してみた振り返りとしては、ざっくり3ステップで構成されています。

ステップ1:PRデータを一括抽出

GitHub APIを使って、自分が関わったPRを期間指定で取得し、スプレッドシートに出力します。私の場合は、スプリント単位で取得して一覧化してます。

ステップ2:全体を俯瞰する

スプレッドシートで、タイトル・URL・変更行数などを一覧表示。大きな変更や気になるPRをピックアップします。

ステップ3:AIと対話的に深掘り

気になったPRについて、GitHub MCP ServerとClaudeを使って詳細を分析。「なぜこの実装にしたのか」「どんな課題を解決したのか」を対話しながら振り返ります。

ステップ1のPRデータの抽出について

これは単純に、GitHub API を使って自分が関わった PR を取得する Python スクリプトを作成します(これはどんな手法でもいいと思います)。

取得する情報

  • 自分が作成したPR
  • 自分がapproveしたPR(レビューで関わったPR)

そして、それらの PR の中でも下記の情報を取得します。

  • タイトル
  • URL(クリック可能なハイパーリンク)
  • 種別(Created / Approved)
  • 追加行数
  • 削除行数
  • 総変更行数

実際の取得スクリプト

設定部分

# ========== 設定 ==========
GITHUB_TOKEN = "your_token_here"  # GitHub Personal Access Token
REPO = "owner/repo"  # リポジトリ
USERNAME = "your_username"  # GitHubユーザー名
START_DATE = "2024-01-01"  # 開始日
END_DATE = "2024-12-31"  # 終了日
# ==========================

PR の取得

def get_prs(token, repo, username, start_date, end_date):
    """PRを取得"""
    headers = {
        'Authorization': f'token {token}',
        'Accept': 'application/vnd.github.v3+json'
    }
    
    # PRを取得(期間で絞り込み)
    url = f'https://api.github.com/repos/{repo}/pulls'
    params = {
        'state': 'all',
        'per_page': 100,
        'sort': 'created',
        'direction': 'desc'
    }
    
    all_prs = []
    page = 1
    
    print(f"PRを取得中...")
    while True:
        params['page'] = page
        response = requests.get(url, headers=headers, params=params)
        
        if response.status_code != 200:
            print(f"エラー: {response.status_code}")
            print(response.text)
            sys.exit(1)
        
        prs = response.json()
        if not prs:
            break
        
        for pr in prs:
            created_at = datetime.strptime(pr['created_at'], '%Y-%m-%dT%H:%M:%SZ')
            
            # 期間外なら終了
            if created_at < start_date:
                return all_prs
            
            # 期間内なら追加
            if start_date <= created_at <= end_date:
                all_prs.append(pr)
        
        page += 1
        if page > 10:  # 安全装置
            break
    
    return all_prs

スプレッドシートへの出力

def export_to_xlsx(data, output_file):
    """XLSXに出力"""
    wb = Workbook()
    sheet = wb.active
    sheet.title = 'GitHub PRs'
    
    # ヘッダー
    headers = ['タイトル', 'URL', '種別', '追加行数', '削除行数', '総変更行数']
    sheet.append(headers)
    
    # ヘッダーのスタイル
    for col in range(1, 7):
        cell = sheet.cell(1, col)
        cell.font = Font(bold=True)
        cell.alignment = Alignment(horizontal='center')
    
    # データ追加(URLはハイパーリンクとして設定)
    for row_idx, row in enumerate(data, start=2):
        title, url, kind, additions, deletions, total = row
        
        # タイトル
        sheet.cell(row_idx, 1, title)
        
        # URL(ハイパーリンク)
        url_cell = sheet.cell(row_idx, 2, url)
        url_cell.hyperlink = url
        url_cell.font = Font(color="0563C1", underline="single")  # 青色+下線
        
        # その他の列
        sheet.cell(row_idx, 3, kind)
        sheet.cell(row_idx, 4, additions)
        sheet.cell(row_idx, 5, deletions)
        sheet.cell(row_idx, 6, total)
    
    # 列幅調整
    sheet.column_dimensions['A'].width = 60
    sheet.column_dimensions['B'].width = 80
    sheet.column_dimensions['C'].width = 15
    sheet.column_dimensions['D'].width = 12
    sheet.column_dimensions['E'].width = 12
    sheet.column_dimensions['F'].width = 15
    
    wb.save(output_file)

コード

github_pr_export.py
#!/usr/bin/env python3
"""
GitHub PRを取得してXLSXに出力するスクリプト
"""
import requests
from datetime import datetime
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment
import sys

# ========== 設定 ==========
GITHUB_TOKEN = "your_token_here"  # GitHub Personal Access Token
REPO = "owner/repo"  # リポジトリ (例: "facebook/react")
USERNAME = "your_username"  # あなたのGitHubユーザー名
START_DATE = "2024-01-01"  # 開始日 (YYYY-MM-DD)
END_DATE = "2024-12-31"  # 終了日 (YYYY-MM-DD)
# ==========================

def get_prs(token, repo, username, start_date, end_date):
    """PRを取得"""
    headers = {
        'Authorization': f'token {token}',
        'Accept': 'application/vnd.github.v3+json'
    }
    
    # PRを取得(期間で絞り込み)
    url = f'https://api.github.com/repos/{repo}/pulls'
    params = {
        'state': 'all',
        'per_page': 100,
        'sort': 'created',
        'direction': 'desc'
    }
    
    all_prs = []
    page = 1
    
    print(f"PRを取得中...")
    while True:
        params['page'] = page
        response = requests.get(url, headers=headers, params=params)
        
        if response.status_code != 200:
            print(f"エラー: {response.status_code}")
            print(response.text)
            sys.exit(1)
        
        prs = response.json()
        if not prs:
            break
        
        for pr in prs:
            created_at = datetime.strptime(pr['created_at'], '%Y-%m-%dT%H:%M:%SZ')
            
            # 期間外なら終了
            if created_at < start_date:
                return all_prs
            
            # 期間内なら追加
            if start_date <= created_at <= end_date:
                all_prs.append(pr)
        
        page += 1
        if page > 10:  # 安全装置
            break
    
    return all_prs

def check_if_approved(token, repo, pr_number, username):
    """自分がapproveしたかチェック"""
    headers = {
        'Authorization': f'token {token}',
        'Accept': 'application/vnd.github.v3+json'
    }
    
    url = f'https://api.github.com/repos/{repo}/pulls/{pr_number}/reviews'
    response = requests.get(url, headers=headers)
    
    if response.status_code != 200:
        return False
    
    reviews = response.json()
    for review in reviews:
        if review['user']['login'] == username and review['state'] == 'APPROVED':
            return True
    
    return False

def get_pr_details(token, repo, pr_number):
    """PRの詳細情報を取得"""
    headers = {
        'Authorization': f'token {token}',
        'Accept': 'application/vnd.github.v3+json'
    }
    
    url = f'https://api.github.com/repos/{repo}/pulls/{pr_number}'
    response = requests.get(url, headers=headers)
    
    if response.status_code != 200:
        return None
    
    return response.json()

def export_to_xlsx(data, output_file):
    """XLSXに出力"""
    wb = Workbook()
    sheet = wb.active
    sheet.title = 'GitHub PRs'
    
    # ヘッダー
    headers = ['タイトル', 'URL', '種別', '追加行数', '削除行数', '総変更行数']
    sheet.append(headers)
    
    # ヘッダーのスタイル
    for col in range(1, 7):
        cell = sheet.cell(1, col)
        cell.font = Font(bold=True)
        cell.alignment = Alignment(horizontal='center')
    
    # データ追加(URLはハイパーリンクとして設定)
    for row_idx, row in enumerate(data, start=2):
        title, url, kind, additions, deletions, total = row
        
        # タイトル
        sheet.cell(row_idx, 1, title)
        
        # URL(ハイパーリンク)
        url_cell = sheet.cell(row_idx, 2, url)
        url_cell.hyperlink = url
        url_cell.font = Font(color="0563C1", underline="single")  # 青色+下線
        
        # その他の列
        sheet.cell(row_idx, 3, kind)
        sheet.cell(row_idx, 4, additions)
        sheet.cell(row_idx, 5, deletions)
        sheet.cell(row_idx, 6, total)
    
    # 列幅調整
    sheet.column_dimensions['A'].width = 60
    sheet.column_dimensions['B'].width = 80
    sheet.column_dimensions['C'].width = 15
    sheet.column_dimensions['D'].width = 12
    sheet.column_dimensions['E'].width = 12
    sheet.column_dimensions['F'].width = 15
    
    wb.save(output_file)
    print(f"\n出力完了: {output_file}")

def main():
    print("=== GitHub PR Export ===\n")
    
    # 設定を使用
    token = GITHUB_TOKEN
    repo = REPO
    username = USERNAME
    start_date_str = START_DATE
    end_date_str = END_DATE
    
    # 日付変換
    start_date = datetime.strptime(start_date_str, '%Y-%m-%d')
    end_date = datetime.strptime(end_date_str + ' 23:59:59', '%Y-%m-%d %H:%M:%S')
    
    print(f"リポジトリ: {repo}")
    print(f"ユーザー: {username}")
    print(f"対象期間: {start_date_str}{end_date_str}\n")
    
    # PRを取得
    prs = get_prs(token, repo, username, start_date, end_date)
    print(f"取得したPR数: {len(prs)}")
    
    # データ整理
    results = []
    
    for i, pr in enumerate(prs, 1):
        print(f"\r処理中... {i}/{len(prs)}", end='', flush=True)
        
        # PR詳細を取得
        pr_details = get_pr_details(token, repo, pr['number'])
        if pr_details is None:
            continue
        
        title = pr_details['title']
        url = pr_details['html_url']
        author = pr_details['user']['login']
        additions = pr_details.get('additions', 0)
        deletions = pr_details.get('deletions', 0)
        total_changes = additions + deletions
        
        # 自分が作成したPR
        if author == username:
            results.append([title, url, 'Created', additions, deletions, total_changes])
        
        # 自分がapproveしたPR
        if check_if_approved(token, repo, pr['number'], username):
            # すでにCreatedとして追加されていなければ追加
            if author != username:
                results.append([title, url, 'Approved', additions, deletions, total_changes])
    
    print(f"\n\n該当PR数: {len(results)}")
    
    # XLSX出力
    import os
    os.makedirs('output', exist_ok=True)
    output_file = 'output/github_prs.xlsx'
    export_to_xlsx(results, output_file)

if __name__ == '__main__':
    main()

Docker化

ローカルの Python を利用して実行するでも大丈夫ですが、Docker を立ち上げて実行した方が楽なので、Docker 化しておくと便利です。

↓みたいな形で簡単に構築できます。

FROM python:3.12-slim

WORKDIR /app

RUN pip install requests openpyxl

COPY github_pr_export.py .

CMD ["python", "github_pr_export.py"]
version: '3.8'

services:
  github-pr-export:
    build: .
    volumes:
      - ./output:/app/output
    environment:
      - TZ=Asia/Tokyo

↓実行コマンド

docker-compose up --build

コマンドを実行すると、 output/github_prs.xlsxにスプレッドシートが出力されます。

スプレッドシートで全体を俯瞰

実際にはこんな感じのものが出力されてきます。

タイトル URL 種別 追加行数 削除行数 総変更行数
ユーザープロフィール機能の実装 [リンク] Created 523 12 535
認証ロジックのリファクタリング [リンク] Approved 89 145 234
バグ修正: ログイン時のエラー [リンク] Created 5 3 8

タイトルを見ると内容を思い出すはずなので、これらの指標ざっと見ることで、どれを振り返るべきなのかとかは意外とすぐに見つけられます。

MCP サーバーで対話的に分析

ピックアップしたPRについて、GitHub公式のMCPサーバー + Claudeで深掘りしていきます。GitHub は、公式の MCP サーバーを公開しているのでそれを利用するだけで簡単に対話形式で振り返ることができます。

GitHub の MCP サーバーについて

GitHub が公式で提供している MCP サーバーを使うと、Claude が GitHub のデータに直接アクセスできるようになります。

GitHub MCPサーバーでできること

  • PRやIssueの検索・取得
  • コードの差分(diff)の確認
  • ファイル内容の取得
  • レビューコメントの確認
  • CI/CDの実行状況確認

セットアップ

Claude Desktopの設定ファイルを編集します:

↓のような内容をローカルの claude_desktop_config.json へセットすれば、Claude Desktop からすぐに GitHub へアクセスできます。

{
  "mcpServers": {
    "github": {
      "command": "/path/to/npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "your_token_here"
      }
    }
  }
}

対話例

PR タイトルなどをコピーした上で、

  • PRのコード差分を見てどんな対応だったのかをまとめてくれる?
  • このPRをいま振り返ってみて、改善できたものは何かある?

など、対話を通じて振り返ることができます。

下記は対話の例です。

接頭辞として xxx とついた PR を取得してきて、この PR でコミットやPR文、コード差分でもっとこうしたほうが良かったとか意見があれば教えて欲しい

という問いを投げかけています。

実装としては、基本的な UI の実装と少し動きをつけるようなロジックが入っているような対応がなされているものです。(言語は、React になります)

良かった点として

のようにフィードバックをしてくれています。他にも改善点を多く挙げてくれていて、例えば、下記のようなフィードバックを返してくれています。

これらの問いに対して、さらに深めて対話することで解像度が高く振り返ることができます。

これからの展望

今回の振り返りをやってみて、やはり、AI分析を前提とした開発フローを入れていくことの重要性に気づきました。より品質の高い分析ができるようになれば、個人単位ではなく、チーム単位にも展開できるようになるため、もっと開発フローにも AI が読みやすい内容を組み込んでいかなければならないなと思いました。

コミットメッセージの最適化

例えば、コミットメッセージ1つでもAIが理解しやすい形にしておくことで、GitHub API の範囲内でもっと簡潔に分析できるようになりそうです(深掘りする PR の量を減らせる)

改善前:

fix bug

改善後:

fix: ログイン時の認証エラーハンドリングを修正

- エラー発生時に適切なエラーメッセージを表示

PR Description を充実させる

PR の Description に下記のような情報を入れておくことでより分析が楽になるのでは、と考えています。

  • なぜその差分になったのか
  • どのような方針で変更したか
  • テストをどのように行なったか
  • 実際に動作を確認するにはどうすればいいか
  • 他のアプリケーションに依存がないか

なぜ、なにを、どのようにの3点が明確に書かれていれば分析しやすくなるのではないかと思ってます。

来年に向けて

Git commit や PR の Description の評価や作成を開発フローに自動かつ自然に導入できれば、個人レベルからチームレベルまで開発生産性の評価が可能になりそうなので、やってみたいなと思っております。

まとめ

いかがでしたでしょうか。今回は、GitHub APIとAIを活用した振り返り手法を紹介しました。

AI の進歩、知見が広まったことによって、ズボラな私でも簡単に年間を通じた振り返りができるようになりました。ぜひ皆さんも年間を通してやったことを振り返ってみてはいかがでしょうか。


参考リンク

Discussion