MCPとエージェントAI：自律型AIを構築する | 第3章：NotionとSlackでプロジェクト管理

## はじめに

第2章では、**Model Context Protocol（MCP）**を使ってGoogle Calendarと連携し、スケジュール管理の基盤となるエージェントAIを構築しました。これにより、予定の取得や新しいミーティングの追加が可能になりました。今回は、さらに実用的なエージェントAIを構築するために、**Notion**と**Slack**をMCPで統合し、プロジェクト管理を自動化する方法を解説します。

この第3章では、Slackのメッセージを読み取り、Notionにタスクを自動追加するMCPサーバーを構築します。たとえば、Slackで「新しいタスク：ドキュメントを明日までに作成」と投稿すると、エージェントAIがNotionのタスクデータベースにそのタスクを追加します。コード例とステップごとのガイドで、実際のワークフローを自動化するプロセスを学びましょう。さあ、始めましょう！

## 準備：NotionとSlack APIのセットアップ

NotionとSlackをMCPサーバーに統合するには、両方のAPIを設定する必要があります。以下の手順で準備します：

### 1. Notion APIの設定
- **Notion Integrationの作成**：
  - [NotionのIntegrationsページ](https://www.notion.so/my-integrations)にアクセス。
  - 新しいIntegrationを作成し、トークンを取得（`NOTION_TOKEN`）。
  - 対象のNotionデータベースにIntegrationを共有（データベースの「共有」メニューで追加）。
- **必要な情報**：
  - Integrationトークン。
  - タスクを追加するデータベースのID（データベースURLの`database_id`部分）。
- **ライブラリのインストール**：
  ```bash
  pip install notion-client
  ```

### 2. Slack APIの設定
- **Slack Appの作成**：
  - [Slack API](https://api.slack.com/apps)にアクセス。
  - 新しいAppを作成し、Bot User OAuth Token（`xoxb-`で始まる）を生成。
  - 必要なスコープを追加：`channels:history`（メッセージ読み取り）、`chat:write`（メッセージ送信）。
  - Appを対象のワークスペースにインストール。
- **必要な情報**：
  - Botトークン（`SLACK_TOKEN`）。
  - メッセージを監視するチャンネルID。
- **ライブラリのインストール**：
  ```bash
  pip install slack-sdk
  ```

### 3. 環境変数の設定
以下の環境変数を`.env`ファイルまたはシステムに設定：
```bash
export NOTION_TOKEN=your_notion_token
export SLACK_TOKEN=your_slack_token
export NOTION_DATABASE_ID=your_database_id
export SLACK_CHANNEL_ID=your_channel_id
```

ライブラリ`python-dotenv`をインストールして環境変数を読み込む：
```bash
pip install python-dotenv
```

## MCPサーバーの構築：NotionとSlackの統合

以下のMCPサーバーは、Slackのメッセージを読み取り、Notionにタスクを追加する機能を提供します。具体的には、Slackメッセージに「タスク：」というキーワードが含まれている場合、その内容をNotionのデータベースにタスクとして追加します。

```python
from mcp import MCPServer
from slack_sdk import WebClient
from notion_client import Client as NotionClient
from datetime import datetime
import os
from dotenv import load_dotenv

class ProjectManagerServer(MCPServer):
    def __init__(self, host, port, slack_token, notion_token, notion_database_id, slack_channel_id):
        super().__init__(host, port)
        self.slack_client = WebClient(token=slack_token)
        self.notion_client = NotionClient(auth=notion_token)
        self.database_id = notion_database_id
        self.channel_id = slack_channel_id
        self.register_resource("get_slack_messages", self.get_slack_messages)
        self.register_tool("create_notion_task", self.create_notion_task)

    def get_slack_messages(self, params):
        try:
            result = self.slack_client.conversations_history(
                channel=self.channel_id,
                limit=10
            )
            messages = [
                {"user": msg["user"], "text": msg["text"], "ts": msg["ts"]}
                for msg in result["messages"]
                if "タスク：" in msg["text"]
            ]
            return {"status": "success", "messages": messages}
        except Exception as e:
            return {"status": "error", "message": str(e)}

    def create_notion_task(self, params):
        try:
            task_name = params.get("task_name", "Unnamed Task")
            due_date = params.get("due_date", datetime.utcnow().isoformat())
            self.notion_client.pages.create(
                parent={"database_id": self.database_id},
                properties={
                    "Name": {"title": [{"text": {"content": task_name}}]},
                    "Due Date": {"date": {"start": due_date}}
                }
            )
            return {"status": "success", "message": f"タスク '{task_name}' をNotionに追加しました"}
        except Exception as e:
            return {"status": "error", "message": str(e)}

if __name__ == "__main__":
    load_dotenv()
    server = ProjectManagerServer(
        host="localhost",
        port=8089,
        slack_token=os.getenv("SLACK_TOKEN"),
        notion_token=os.getenv("NOTION_TOKEN"),
        notion_database_id=os.getenv("NOTION_DATABASE_ID"),
        slack_channel_id=os.getenv("SLACK_CHANNEL_ID")
    )
    print("プロジェクト管理MCPサーバーを起動中: http://localhost:8089")
    server.start()
```

### コードの説明
- **get_slack_messages**：指定チャンネルの最新10件のメッセージを取得し、「タスク：」を含むメッセージをフィルタリング。
- **create_notion_task**：Notionデータベースに新しいタスクを追加。タスク名と期限をパラメータとして受け取る。
- **SlackとNotionのクライアント**：それぞれのAPIを初期化し、認証情報を環境変数から取得。
- **register_resource/tool**：メッセージ取得をリソース、タスク作成をツールとして登録。

### 前提条件
- Notionデータベースに「Name」（タイトル）と「Due Date」（日付）のプロパティが存在。
- SlackチャンネルにBotが招待済み（`/invite @BotName`）。

## サーバーのテスト

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

1. **サーバー起動**：
   ```bash
   python project_manager_server.py
   ```
   コンソールに「プロジェクト管理MCPサーバーを起動中: http://localhost:8089」と表示。

2. **Slackメッセージのテスト**：
   Pythonでリクエストを送信：
   ```python
   import requests
   import json

   url = "http://localhost:8089"
   payload = {
       "jsonrpc": "2.0",
       "method": "get_slack_messages",
       "params": {},
       "id": 1
   }
   response = requests.post(url, json=payload)
   print(json.dumps(response.json(), indent=2, ensure_ascii=False))
   ```
   期待されるレスポンス（Slackに「タスク：ドキュメント作成」がある場合）：
   ```json
   {
     "jsonrpc": "2.0",
     "result": {
       "status": "success",
       "messages": [
         {
           "user": "U123456",
           "text": "タスク：ドキュメント作成",
           "ts": "1623456789.000100"
         }
       ]
     },
     "id": 1
   }
   ```

3. **Notionタスク作成のテスト**：
   ```python
   payload = {
       "jsonrpc": "2.0",
       "method": "create_notion_task",
       "params": {
           "task_name": "ドキュメント作成",
           "due_date": "2025-04-16T00: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",
       "message": "タスク 'ドキュメント作成' をNotionに追加しました"
     },
     "id": 2
   }
   ```

## Claude Desktopとの統合

サーバーをClaude Desktopに接続し、エージェントAIとして動作させます：

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

2. **Claudeでテスト**：
   Claude Desktopを起動し、プロンプトを入力：
   ```
   Slackのタスクをチェックして、Notionに追加してください。
   ```
   レスポンス例：
   ```
   Slackから以下のタスクを見つけました：
   - タスク：ドキュメント作成
   Notionにタスク「ドキュメント作成」を追加しました（期限：2025-04-16）。
   ```

   別のプロンプト：
   ```
   新しいタスク「プレゼン準備」をNotionに追加してください。
   ```
   レスポンス例：
   ```
   タスク「プレゼン準備」をNotionに追加しました（期限：今日）。
   ```

## エージェントロジックの構築

実際のエージェントAIでは、Slackメッセージを自動監視し、タスクをリアルタイムでNotionに追加するロジックが必要です。以下は、簡略化したロジック例（本番ではSlackのEvents APIを使用）：

```python
def process_slack_messages(self):
    messages = self.get_slack_messages({})["messages"]
    for msg in messages:
        if "タスク：" in msg["text"]:
            task_name = msg["text"].split("タスク：")[1].strip()
            self.create_notion_task({"task_name": task_name})
            self.slack_client.chat_postMessage(
                channel=self.channel_id,
                text=f"タスク「{task_name}」をNotionに追加しました！"
            )
```

このロジックをサーバーに追加するには、Slack Events APIを設定し、メッセージイベントをトリガーにします。

## 実装のコツと注意点

- **エラーハンドリング**：NotionやSlackのAPI制限（レートリミット）に注意。エラーが発生した場合、ユーザーにわかりやすいメッセージを返す。
- **重複防止**：同じタスクが複数回追加されないよう、メッセージのタイムスタンプをチェック。
- **セキュリティ**：本番環境では、OAuth 2.1を有効化し、`auth: none`を避ける。
- **テスト**：テスト用SlackチャンネルとNotionデータベースを作成し、本番データに影響を与えないように。
- **拡張性**：タスクに優先度や担当者を追加するプロパティをNotionデータベースに設定。

## 試してみよう：挑戦課題
以下の機能を追加して、エージェントを強化してみてください：
- Slackメッセージに「期限：YYYY-MM-DD」を含む場合、Notionにその期限を設定。
- タスク追加後、SlackにNotionのタスクページへのリンクを返信。
- Notionのタスク一覧を取得するリソースを追加。

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

この第3章では、NotionとSlackをMCPで統合し、プロジェクト管理を自動化するエージェントAIを構築しました。Slackのメッセージからタスクを抽出し、Notionに追加する機能は、チームのワークフローを効率化する強力な一歩です。

次の第4章では、リアルタイムデータを取り入れてエージェントをさらに強化します。具体的には、**Brave Search**と**Qdrant**を統合し、最新情報やベクトルデータに基づく回答を提供するエージェントを構築します。リアルタイムの可能性に興味がある方は、ぜひお楽しみに！

---

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