1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

はじめに

第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ページにアクセス。
    • 新しいIntegrationを作成し、トークンを取得(NOTION_TOKEN)。
    • 対象のNotionデータベースにIntegrationを共有(データベースの「共有」メニューで追加)。
  • 必要な情報
    • Integrationトークン。
    • タスクを追加するデータベースのID(データベースURLのdatabase_id部分)。
  • ライブラリのインストール
    pip install notion-client
    

2. Slack APIの設定

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

3. 環境変数の設定

以下の環境変数を.envファイルまたはシステムに設定:

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をインストールして環境変数を読み込む:

pip install python-dotenv

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

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

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. サーバー起動

    python project_manager_server.py
    

    コンソールに「プロジェクト管理MCPサーバーを起動中: http://localhost:8089」と表示。

  2. Slackメッセージのテスト
    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に「タスク:ドキュメント作成」がある場合):

    {
      "jsonrpc": "2.0",
      "result": {
        "status": "success",
        "messages": [
          {
            "user": "U123456",
            "text": "タスク:ドキュメント作成",
            "ts": "1623456789.000100"
          }
        ]
      },
      "id": 1
    }
    
  3. Notionタスク作成のテスト

    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))
    

    期待されるレスポンス:

    {
      "jsonrpc": "2.0",
      "result": {
        "status": "success",
        "message": "タスク 'ドキュメント作成' をNotionに追加しました"
      },
      "id": 2
    }
    

Claude Desktopとの統合

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

  1. 設定ファイルの編集
    Claude Desktopの設定ファイル(例:claude_desktop_config.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を使用):

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 SearchQdrantを統合し、最新情報やベクトルデータに基づく回答を提供するエージェントを構築します。リアルタイムの可能性に興味がある方は、ぜひお楽しみに!


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

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?