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を構築する | 第2章:Google Calendarとの連携

Posted at

はじめに

第1章では、エージェントAIの概念と、**Model Context Protocol(MCP)**がどのようにAIを外部データやツールと接続し、自律的なタスク実行を可能にするかを学びました。簡単なメールサーバーを構築し、MCPの基本的な動作を体験しました。今回は、実際にエージェントAIを強化する第一歩として、Google CalendarとMCPを統合する方法を解説します。

この第2章では、Google Calendar APIを利用して予定を取得・作成するMCPサーバーを構築し、Claude Desktopと連携させます。これにより、「来週の空き時間を教えて」や「新しいミーティングを追加して」といったリクエストを処理できるエージェントAIの基盤を作ります。コード例とステップごとのガイドで、初心者でも実装できる内容を目指します。さっそく始めましょう!

Google Calendar APIの準備

Google Calendarと連携するには、Google Cloud Platform(GCP)でAPIを有効化し、認証情報を設定する必要があります。以下の手順で準備します:

  1. Google Cloud Consoleでプロジェクトを作成

    • Google Cloud Consoleにアクセス。
    • 新しいプロジェクトを作成(例:mcp-calendar-agent)。
    • 「APIとサービス」→「ライブラリ」で「Google Calendar API」を検索し、有効化。
  2. OAuth 2.0認証情報の設定

    • 「APIとサービス」→「認証情報」→「認証情報を作成」→「OAuthクライアントID」。
    • アプリケーションの種類を「デスクトップアプリ」に設定。
    • クライアントIDとクライアントシークレットをダウンロード(credentials.json)。
  3. 必要なライブラリのインストール
    PythonでGoogle Calendar APIを利用するため、以下のライブラリをインストール:

    pip install google-auth-oauthlib google-auth-httplib2 google-api-python-client
    
  4. トークンの生成
    初回実行時に、ブラウザでGoogleアカウントにログインし、アクセスを許可。トークンがtoken.jsonに保存されます。

MCPサーバーの構築:Google Calendarとの統合

Google Calendarから予定を取得し、新しい予定を作成するMCPサーバーを構築します。以下のコードは、予定リストの取得と予定作成のリソースを提供します:

from mcp import MCPServer
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from datetime import datetime, timedelta
import os

class CalendarServer(MCPServer):
    def __init__(self, host, port, credentials_file, token_file):
        super().__init__(host, port)
        self.credentials_file = credentials_file
        self.token_file = token_file
        self.service = self._init_calendar_service()
        self.register_resource("get_events", self.get_events)
        self.register_resource("create_event", self.create_event)

    def _init_calendar_service(self):
        SCOPES = ['https://www.googleapis.com/auth/calendar']
        creds = None
        if os.path.exists(self.token_file):
            creds = Credentials.from_authorized_user_file(self.token_file, SCOPES)
        if not creds or not creds.valid:
            flow = InstalledAppFlow.from_client_secrets_file(self.credentials_file, SCOPES)
            creds = flow.run_local_server(port=0)
            with open(self.token_file, 'w') as token:
                token.write(creds.to_json())
        return build('calendar', 'v3', credentials=creds)

    def get_events(self, params):
        try:
            time_min = datetime.utcnow().isoformat() + 'Z'
            time_max = (datetime.utcnow() + timedelta(days=7)).isoformat() + 'Z'
            events_result = self.service.events().list(
                calendarId='primary',
                timeMin=time_min,
                timeMax=time_max,
                maxResults=10,
                singleEvents=True,
                orderBy='startTime'
            ).execute()
            events = events_result.get('items', [])
            formatted_events = [
                {
                    'summary': event.get('summary', 'No title'),
                    'start': event['start'].get('dateTime', event['start'].get('date')),
                    'end': event['end'].get('dateTime', event['end'].get('date'))
                }
                for event in events
            ]
            return {"status": "success", "events": formatted_events}
        except Exception as e:
            return {"status": "error", "message": str(e)}

    def create_event(self, params):
        try:
            summary = params.get("summary", "New Meeting")
            start_time = params.get("start_time", (datetime.utcnow() + timedelta(hours=1)).isoformat())
            duration = params.get("duration", 60)  # 分単位
            event = {
                'summary': summary,
                'start': {'dateTime': start_time, 'timeZone': 'UTC'},
                'end': {'dateTime': (datetime.fromisoformat(start_time) + timedelta(minutes=duration)).isoformat(), 'timeZone': 'UTC'}
            }
            event = self.service.events().insert(calendarId='primary', body=event).execute()
            return {"status": "success", "event_id": event.get('id'), "link": event.get('htmlLink')}
        except Exception as e:
            return {"status": "error", "message": str(e)}

if __name__ == "__main__":
    server = CalendarServer(
        host="localhost",
        port=8088,
        credentials_file="credentials.json",
        token_file="token.json"
    )
    print("カレンダーMCPサーバーを起動中: http://localhost:8088")
    server.start()

コードの説明

  • _init_calendar_service:Google Calendar APIの認証を設定。初回はブラウザで認証し、トークンを保存。
  • get_events:今後7日間の予定を取得し、タイトル、開始・終了時間を返す。
  • create_event:新しい予定を作成。タイトル、開始時間、期間をパラメータとして受け取る。
  • register_resource:リソースを登録し、AIがアクセス可能に。

前提条件

  • credentials.jsonをプロジェクトフォルダに配置。
  • 初回実行時に、ブラウザでGoogleアカウントにログインし、アクセスを許可。

サーバーのテスト

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

  1. サーバー起動

    python calendar_server.py
    

    コンソールに「カレンダーMCPサーバーを起動中: http://localhost:8088」と表示。

  2. JSON-RPCリクエストの送信
    Pythonでテストリクエストを送信:

    import requests
    import json
    
    url = "http://localhost:8088"
    payload = {
        "jsonrpc": "2.0",
        "method": "get_events",
        "params": {},
        "id": 1
    }
    response = requests.post(url, json=payload)
    print(json.dumps(response.json(), indent=2, ensure_ascii=False))
    

    期待されるレスポンス:

    {
      "jsonrpc": "2.0",
      "result": {
        "status": "success",
        "events": [
          {
            "summary": "チームミーティング",
            "start": "2025-04-16T10:00:00Z",
            "end": "2025-04-16T11:00:00Z"
          }
        ]
      },
      "id": 1
    }
    
  3. 予定作成のテスト

    payload = {
        "jsonrpc": "2.0",
        "method": "create_event",
        "params": {"summary": "テストミーティング", "duration": 30},
        "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)に以下を追加:

    {
      "mcp_servers": [
        {
          "name": "CalendarServer",
          "url": "http://localhost:8088",
          "auth": "none"
        }
      ]
    }
    
  2. Claudeでテスト
    Claude Desktopを起動し、プロンプトを入力:

    来週の予定を教えてください。
    

    レスポンス例:

    来週の予定:
    - 2025-04-16 10:00: チームミーティング(1時間)
    

    予定作成のプロンプト:

    明日の14時に30分のミーティングを追加してください。
    

    レスポンス例:

    ミーティングを追加しました:
    - タイトル:テストミーティング
    - 時間:2025-04-16 14:00(30分)
    - リンク:https://calendar.google.com/...
    

セキュリティの考慮

本番環境では、以下の対策を講じます:

  • OAuth 2.1の使用auth"none"から適切なトークン認証に変更。
  • アクセス制限:カレンダーの読み取り専用スコープ(https://www.googleapis.com/auth/calendar.readonly)を検討。
  • エラーハンドリング:ユーザー向けにわかりやすいエラーメッセージを返す。
  • ログ記録:リクエストとレスポンスを記録し、問題を追跡。

実装のコツ

  • キャッシュ:頻繁にアクセスする予定データをキャッシュして、API呼び出しを削減。
  • タイムゾーン対応:ユーザーのタイムゾーンを考慮し、適切に変換。
  • テストデータ:本番前に、テスト用カレンダーを作成して動作確認。
  • コミュニティリソース:GitHubで公開されているGoogle Calendar用MCPサーバーを参考に。

まとめと次のステップ

この第2章では、Google CalendarとMCPを統合し、スケジュール管理の基盤となるサーバーを構築しました。これにより、エージェントAIがユーザーの予定を把握し、新しい予定を追加する能力を獲得しました。

次の第3章では、NotionとSlackを統合し、プロジェクト管理を自動化するエージェントAIを構築します。たとえば、SlackのメッセージからNotionにタスクを自動追加する機能を実装します。実用的なエージェントAIに興味がある方は、ぜひお楽しみに!


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

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?