FigmaでAIを強化する | 第4章：バージョン管理AIの強化：リアルタイム連携

## はじめに

第3章では、**Figma**のデザインデータを活用してUI分析エージェントを構築し、色のコントラストやフォントサイズの整合性を評価しました。これにより、AIがUIの品質を自動チェックし、改善提案を生成できるようになりました。今回は、Figmaのバージョン管理機能を強化する**バージョン管理AI**を構築します。このAIは、デザインの変更履歴を追跡し、変更点をリアルタイムで捕捉してコメントとして記録します。

この第4章では、FigmaのWebhook機能とMCPサーバーを統合し、ファイルの更新（例：新しいバージョンやノード変更）をリアルタイムで検知します。AIはこれを利用して、変更の概要をコメントしたり、重要な変更をチームに通知したりできます。コード例とステップごとのガイドで、バージョン管理AIの構築を体験しましょう。さあ、始めましょう！

## バージョン管理AIとは？

バージョン管理AIは、Figmaファイルの変更履歴を監視し、デザインの進化を効率的に管理するエージェントです。MCPサーバーとFigmaのWebhookを組み合わせることで、以下のような機能を実現できます：
- **変更検知**：新しいバージョンやノードの変更をリアルタイムで捕捉。
- **コメント生成**：変更内容を要約し、Figmaにコメントとして投稿。
- **通知**：重要な変更をチームに通知（例：Slackやメール）。

### ユースケース
- デザイン管理：変更履歴を自動記録し、デザインレビューの効率を向上。
- チームコラボレーション：リモートチームに変更点を即座に共有。
- 品質管理：意図しない変更（例：ブランドカラーからの逸脱）を検出。

## 開発環境の準備

第3章の環境を基に、以下の追加準備を行います：
- **Python 3.8以降**、**mcpライブラリ**、**requestsライブラリ**、**Claude Desktop**：これまでと同じ。
- **webcolorsライブラリ**：第3章から再利用。
- **ngrok**：ローカルサーバーを公開し、Webhookを受信。
- **Figmaファイル**：バージョン管理用のテストデータ。

インストールコマンド（必要に応じて）：
```bash
pip install webcolors
```

### Figmaのセットアップ
1. **テストファイルの準備**：
   - 第3章のFigmaファイル（例：`MCP-Design`）を使用。
   - 新しいバージョンを作成：Figmaの「Version History」パネルで「Save as new version」をクリックし、名前を付ける（例：`v1.1`）。
   - ノードを変更（例：ボタンの色を`#007BFF`から`#FF5733`に変更）。
2. **Webhookの設定**：
   - Figmaの「Team Settings」でWebhookを作成（有料プランが必要）。
   - イベント：`FILE_VERSION_UPDATE`を選択。
   - Webhook URL：後でngrokから取得（例：`https://abc123.ngrok.io`）。
   - Webhook Secretを記録（セキュリティ用）。
3. **ngrokの設定**：
   - ngrokをインストール（`brew install ngrok`または公式サイト）。
   - ローカルサーバーを公開：
     ```bash
     ngrok http 8108
     ```
   - 生成されたURLをFigmaのWebhook設定に登録。
4. **環境変数**：
   第3章の`.env`ファイルに以下を追加：
   ```bash
   FIGMA_TOKEN=your_figma_token
   FIGMA_FILE_ID=your_file_id
   FIGMA_WEBHOOK_SECRET=your_webhook_secret
   ```

## コード例：バージョン管理用MCPサーバー

以下のMCPサーバーは、FigmaのWebhookを介してバージョン更新を捕捉し、変更内容を分析してコメントとして投稿します。

```python
from mcp import MCPServer
import os
from dotenv import load_dotenv
import requests
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import hmac
import hashlib
import threading

class FigmaVersionServer(MCPServer):
    def __init__(self, host, port, figma_token, file_id, webhook_secret):
        super().__init__(host, port)
        self.figma_token = figma_token
        self.file_id = file_id
        self.webhook_secret = webhook_secret
        self.headers = {"X-Figma-Token": figma_token}
        self.latest_version = None
        self.register_resource("get_latest_version", self.get_latest_version)
        self.register_tool("add_comment", self.add_comment)
        self.start_webhook_server()

    def get_latest_version(self, params):
        try:
            if self.latest_version:
                version_id = self.latest_version["version_id"]
                url = f"https://api.figma.com/v1/files/{self.file_id}/versions/{version_id}"
                response = requests.get(url, headers=self.headers)
                response.raise_for_status()
                data = response.json()
                version_info = {
                    "version_id": data["id"],
                    "description": data.get("description", ""),
                    "created_at": data["created_at"],
                    "user": data["user"]["handle"]
                }
                return {"status": "success", "version_info": version_info}
            return {"status": "success", "version_info": None, "message": "バージョン更新なし"}
        except Exception as e:
            return {"status": "error", "message": str(e)}

    def add_comment(self, params):
        try:
            message = params.get("message", "")
            node_id = params.get("node_id", "")
            if not message:
                return {"status": "error", "message": "コメントが必要です"}
            
            url = f"https://api.figma.com/v1/files/{self.file_id}/comments"
            payload = {
                "message": message,
                "client_meta": {"node_id": node_id} if node_id else None
            }
            response = requests.post(url, headers=self.headers, json=payload)
            response.raise_for_status()
            comment = response.json()
            return {"status": "success", "comment_id": comment["id"]}
        except Exception as e:
            return {"status": "error", "message": str(e)}

    def start_webhook_server(self):
        class WebhookHandler(BaseHTTPRequestHandler):
            def do_POST(self):
                content_length = int(self.headers["Content-Length"])
                post_data = self.rfile.read(content_length)
                signature = self.headers.get("X-Figma-Signature", "")
                expected_signature = hmac.new(
                    self.server.parent.webhook_secret.encode(),
                    post_data,
                    hashlib.sha256
                ).hexdigest()

                if hmac.compare_digest(signature, expected_signature):
                    self.server.parent.latest_version = json.loads(post_data.decode("utf-8"))
                    self.send_response(200)
                    self.end_headers()
                    self.wfile.write(b"Webhook received")
                else:
                    self.send_response(403)
                    self.end_headers()
                    self.wfile.write(b"Invalid signature")

        server = HTTPServer(("localhost", 8109), WebhookHandler)
        server.parent = self
        threading.Thread(target=server.serve_forever, daemon=True).start()
        print("Webhookサーバーを起動中: http://localhost:8109")

if __name__ == "__main__":
    load_dotenv()
    server = FigmaVersionServer(
        host="localhost",
        port=8108,
        figma_token=os.getenv("FIGMA_TOKEN"),
        file_id=os.getenv("FIGMA_FILE_ID"),
        webhook_secret=os.getenv("FIGMA_WEBHOOK_SECRET")
    )
    print("FigmaバージョンMCPサーバーを起動中: http://localhost:8108")
    server.start()
```

### コードの説明
- **get_latest_version**：Webhookで捕捉した最新のバージョン更新情報を取得し、バージョンID、説明、作成日、ユーザーを返す。
- **add_comment**：第3章から再利用し、変更内容をコメントとして投稿。
- **start_webhook_server**：ローカルWebhookサーバーを起動し、Figmaからの更新を受信。
- **WebhookHandler**：Webhookリクエストを処理。`X-Figma-Signature`で署名を検証し、正当性を確認。

### 前提条件
- Figmaファイルにバージョン履歴が存在。
- FigmaのWebhookが設定済みで、ngrok URLが正しく登録。
- `.env`ファイルに正しい`FIGMA_TOKEN`、`FIGMA_FILE_ID`、`FIGMA_WEBHOOK_SECRET`が設定済み。

## サーバーのテスト

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

1. **ngrok起動**：
   ```bash
   ngrok http 8109
   ```
   ngrok URL（例：`https://abc123.ngrok.io`）をFigmaのWebhook設定に登録。

2. **サーバー起動**：
   ```bash
   python figma_version_server.py
   ```
   コンソールに「FigmaバージョンMCPサーバーを起動中: http://localhost:8108」と「Webhookサーバーを起動中: http://localhost:8109」が表示。

3. **Webhookのテスト**：
   - FigmaのUIで新しいバージョンを保存（例：`v1.2`）。
   - サーバーのコンソールにWebhook受信ログが表示。

4. **最新バージョン取得のテスト**：
   Pythonでリクエストを送信：
   ```python
   import requests
   import json

   url = "http://localhost:8108"
   payload = {
       "jsonrpc": "2.0",
       "method": "get_latest_version",
       "params": {},
       "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",
       "version_info": {
         "version_id": "789",
         "description": "v1.2: ボタンの色を変更",
         "created_at": "2025-04-20T10:00:00Z",
         "user": "DesignerA"
       }
     },
     "id": 1
   }
   ```

5. **コメント追加のテスト**：
   ```python
   payload = {
       "jsonrpc": "2.0",
       "method": "add_comment",
       "params": {
           "message": "ボタンの色が変更されました（#FF5733）",
           "node_id": "1:2"
       },
       "id": 2
   }
   response = requests.post(url, json=payload)
   print(json.dumps(response.json(), indent=2, ensure_ascii=False))
   ```

## Claude Desktopとの接続

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

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

2. **Claudeでテスト**：
   Claude Desktopを起動し、プロンプトを入力：
   ```
   最新のバージョン変更を教えてください。
   ```
   レスポンス例：
   ```
   最新のバージョン変更：
   - バージョン：v1.2
   - 説明：ボタンの色を変更
   - ユーザー：DesignerA
   - 日時：2025-04-20 10:00
   ```

   別のプロンプト：
   ```
   ボタンの色変更をコメントしてください。
   ```
   レスポンス例：
   ```
   ボタンに「ボタンの色が変更されました（#FF5733）」をコメントしました。
   ```

## 実装のコツと注意点

- **Webhookの安定性**：ngrokの無料枠ではセッションが切れる場合がある。本番環境では固定URL（例：AWS Lambda）を使用。
- **レートリミティング**：Figma APIの制限（通常30リクエスト/分）に注意。
- **セキュリティ**：Webhook署名を必ず検証し、不正アクセスを防止。
- **テスト**：テスト用ファイルを作成し、本番データに影響を与えない。
- **拡張性**：大量のバージョンを処理する場合、キャッシュやキュー（例：Redis）を検討。

## 試してみよう：挑戦課題
以下の機能を追加して、エージェントを強化してみてください：
- 変更されたノードの詳細（例：色やサイズ）を分析し、コメントに含める。
- バージョン間の差分を可視化する機能（例：変更されたノードをリスト）。
- Webhookでキャプチャした変更をSlackに通知するツール。

## まとめと次のステップ

この第4章では、FigmaのWebhookを活用してバージョン管理AIを構築しました。デザインの変更をリアルタイムで捕捉し、AIが変更内容をコメントや通知として記録できるようになりました。

次の第5章では、MCPサーバーの**最適化とコミュニティへの貢献**に焦点を当てます。サーバーのパフォーマンス向上、セキュリティ強化、そしてFigma用MCPサーバーをオープンソースとして共有する方法を学びます。コミュニティAIの未来に興味がある方は、ぜひお楽しみに！

---

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