TrelloでAIを強化する | 第2章：タスクを自動化：AIによるアクション

## はじめに

第1章では、**Trello**と**Model Context Protocol（MCP）**の基本を学び、Trelloボードからカードデータを取得するMCPサーバーを構築しました。これにより、AIがタスク情報を取得し、進捗を把握できるようになりました。今回は、この基盤を進化させ、Trelloにカードを追加・更新する機能を実装します。これにより、**タスクを自動化**するエージェントを構築します。

この第2章では、MCPサーバーを通じてAIが新しいタスクを作成したり、既存のカードを移動したり、期限を更新したりできるようにします。たとえば、ユーザーが「新しいタスクを追加して」と依頼すると、AIが適切なリストにカードを追加し、必要に応じて説明や期限を設定します。コード例とステップごとのガイドで、タスク自動化の可能性を体感しましょう。さあ、始めましょう！

## タスク自動化エージェントとは？

タスク自動化エージェントは、Trelloのカードやリストを操作し、プロジェクト管理を効率化するAIです。MCPサーバーを介して、以下のような機能を実現できます：
- **カード作成**：新しいタスクを自動で追加。
- **カード更新**：リスト間での移動、期限や説明の設定。
- **タスク整理**：優先順位や期限に基づいてカードを自動分類。

### ユースケース
- プロジェクト管理：AIが新しいタスクを自動で`To Do`リストに追加。
- タスク整理：期限が近いカードを`In Progress`に移動。
- チーム連携：AIがタスクの説明やコメントを更新し、チームに通知。

## 開発環境の準備

第1章の環境を基に、以下の準備を行います：
- **Python 3.8以降**、**mcpライブラリ**、**requestsライブラリ**、**Claude Desktop**：第1章と同じ。
- **python-dotenv**：環境変数の管理（既にインストール済み）。
- **Trelloボード**：タスク自動化用のボードとリスト。

### Trelloのセットアップ
1. **ボード確認**：
   - 第1章で作成したTrelloボード（例：`MCP-Project`）を使用。
   - リストを確認（例：`To Do`、`In Progress`、`Done`）。
   - 各リストのIDを記録（Trello UIのカードURLやAPIで取得可能）。
2. **APIキーとトークンの確認**：
   - 第1章のAPIキーとトークンを再利用（`read`、`write`スコープが必要）。
3. **環境変数**：
   第1章の`.env`ファイル（`TRELLO_API_KEY`、`TRELLO_TOKEN`、`TRELLO_BOARD_ID`）を再利用。必要に応じてリストIDを追加：
   ```bash
   TRELLO_TO_DO_LIST_ID=your_to_do_list_id
   ```

## コード例：タスク自動化用MCPサーバー

以下のMCPサーバーは、Trelloボードからカードを取得し、新しいカードを追加したり、既存のカードを更新したりする機能を提供します。

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

class TrelloTaskServer(MCPServer):
    def __init__(self, host, port, api_key, token, board_id, to_do_list_id):
        super().__init__(host, port)
        self.api_key = api_key
        self.token = token
        self.board_id = board_id
        self.to_do_list_id = to_do_list_id
        self.base_url = "https://api.trello.com/1"
        self.params = {"key": api_key, "token": token}
        self.register_resource("get_cards", self.get_cards)
        self.register_tool("create_card", self.create_card)
        self.register_tool("update_card", self.update_card)

    def get_cards(self, params):
        try:
            url = f"{self.base_url}/boards/{self.board_id}/cards"
            response = requests.get(url, params=self.params)
            response.raise_for_status()
            cards = [
                {
                    "id": card["id"],
                    "name": card["name"],
                    "desc": card["desc"],
                    "due": card.get("due", ""),
                    "list_id": card["idList"]
                }
                for card in response.json()
            ]
            return {"status": "success", "cards": cards}
        except Exception as e:
            return {"status": "error", "message": str(e)}

    def create_card(self, params):
        try:
            name = params.get("name", "")
            desc = params.get("desc", "")
            due = params.get("due", "")
            if not name:
                return {"status": "error", "message": "カード名が必要です"}
            
            url = f"{self.base_url}/cards"
            payload = {
                "name": name,
                "desc": desc,
                "idList": self.to_do_list_id,
                "due": due if due else None
            }
            response = requests.post(url, params=self.params, json=payload)
            response.raise_for_status()
            card = response.json()
            return {"status": "success", "card_id": card["id"]}
        except Exception as e:
            return {"status": "error", "message": str(e)}

    def update_card(self, params):
        try:
            card_id = params.get("card_id", "")
            list_id = params.get("list_id", "")
            due = params.get("due", "")
            if not card_id:
                return {"status": "error", "message": "カードIDが必要です"}
            
            url = f"{self.base_url}/cards/{card_id}"
            payload = {}
            if list_id:
                payload["idList"] = list_id
            if due:
                payload["due"] = due
            response = requests.put(url, params=self.params, json=payload)
            response.raise_for_status()
            return {"status": "success", "card_id": card_id}
        except Exception as e:
            return {"status": "error", "message": str(e)}

if __name__ == "__main__":
    load_dotenv()
    server = TrelloTaskServer(
        host="localhost",
        port=8112,
        api_key=os.getenv("TRELLO_API_KEY"),
        token=os.getenv("TRELLO_TOKEN"),
        board_id=os.getenv("TRELLO_BOARD_ID"),
        to_do_list_id=os.getenv("TRELLO_TO_DO_LIST_ID")
    )
    print("TrelloタスクMCPサーバーを起動中: http://localhost:8112")
    server.start()
```

### コードの説明
- **get_cards**：第1章から再利用。ボードのカード情報を取得。
- **create_card**：新しいカードを`To Do`リストに追加。名前、説明、期限を指定可能。
- **update_card**：既存のカードを更新（リスト移動や期限変更）。
- **register_resource/tool**：カード取得をリソース、カード作成・更新をツールとして登録。

### 前提条件
- Trelloボードにリスト（例：`To Do`）が存在。
- `.env`ファイルに正しい`TRELLO_API_KEY`、`TRELLO_TOKEN`、`TRELLO_BOARD_ID`、`TRELLO_TO_DO_LIST_ID`が設定済み。
- APIトークンに読み書き quyền限がある。

## サーバーのテスト

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

1. **サーバー起動**：
   ```bash
   python trello_task_server.py
   ```
   コンソールに「TrelloタスクMCPサーバーを起動中: http://localhost:8112」と表示。

2. **カード作成のテスト**：
   Pythonでリクエストを送信：
   ```python
   import requests
   import json

   url = "http://localhost:8112"
   payload = {
       "jsonrpc": "2.0",
       "method": "create_card",
       "params": {
           "name": "新しいタスク",
           "desc": "プロジェクト計画を策定",
           "due": "2025-04-23T10:00:00Z"
       },
       "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",
       "card_id": "card125"
     },
     "id": 1
   }
   ```

3. **カード更新のテスト**：
   ```python
   payload = {
       "jsonrpc": "2.0",
       "method": "update_card",
       "params": {
           "card_id": "card125",
           "list_id": "list457",
           "due": "2025-04-24T10:00:00Z"
       },
       "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",
       "card_id": "card125"
     },
     "id": 2
   }
   ```

## Claude Desktopとの接続

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

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

2. **Claudeでテスト**：
   Claude Desktopを起動し、プロンプトを入力：
   ```
   新しいタスク「プロジェクト計画」をTo Doリストに追加してください。
   ```
   レスポンス例：
   ```
   「プロジェクト計画」をTo Doリストに追加しました。
   ```

   別のプロンプト：
   ```
   カード「card125」をIn Progressリストに移動してください。
   ```
   レスポンス例：
   ```
   カード「card125」をIn Progressリストに移動しました。
   ```

## 実装のコツと注意点

- **エラーハンドリング**：無効なリストIDやカードIDを適切に処理。
- **レートリミティング**：Trello APIの制限（通常100リクエスト/10秒）に注意。
- **セキュリティ**：本番環境では、`auth: none`を避け、トークン認証を導入。
- **テスト**：テスト用ボードを作成し、本番データに影響を与えない。
- **拡張性**：大量のカードを処理する場合、キャッシュ（例：Redis）を検討。

## 試してみよう：挑戦課題
以下の機能を追加して、エージェントを強化してみてください：
- カードにラベルを追加する機能。
- 特定リストのカードだけを取得するパラメータをサポート。
- カードにコメントを追加するツール。

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

この第2章では、Trelloにカードを追加・更新するMCPサーバーを構築し、タスク自動化エージェントを実現しました。AIがタスクを作成・整理できるようになり、プロジェクト管理の効率が向上しました。

次の第3章では、Trelloのデータを活用して**タスク分析エージェント**を構築します。たとえば、AIがタスクの完了率やボトルネックを分析し、チームのパフォーマンスを評価します。タスク分析AIに興味がある方は、ぜひお楽しみに！

---

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