1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JiraでAIを強化する | 第4章:リアルタイム管理AIを強化:Webhook連携

Posted at

はじめに

第3章では、JiraのTicketやSprintデータを活用してプロジェクト分析エージェントを構築しました。進捗やパフォーマンスを分析することで、AIがプロジェクト管理のインサイトを提供し、プロセス改善を支援できるようになりました。今回は、JiraのWebhook機能を利用して、Ticketやステータス変更のイベントをリアルタイムで検知するリアルタイム管理AIを構築します。

この第4章では、MCPサーバーにJiraのWebhookを統合し、Ticket作成やステータス変更などのイベントを即座に捕捉します。AIはこれを利用して、自動コメントを追加したり、イベントを他のツール(例:Slack)に通知したりできます。コード例とステップごとのガイドで、リアル thời gian管理AIの構築を体験しましょう。さあ、始めましょう!

リアルタイム管理AIとは?

リアルタイム管理AIは、Jiraプロジェクトのイベント(Ticket作成、ステータス変更、コメント追加など)を監視し、プロジェクト管理を動的に支援するエージェントです。MCPサーバーとJiraのWebhookを組み合わせることで、以下のような機能を実現できます:

  • イベント検知:新しいTicket、ステータス変更、コメントをリアルタイムで捕捉。
  • 自動応答:特定のイベント(例:Ticket作成)に基づいてコメントや優先度を追加。
  • 外部連携:イベントをSlackやメールに通知し、コラボレーションを強化。

ユースケース

  • プロジェクト管理:AIが「バグ」Ticket作成時に自動で「High」優先度を付ける。
  • チーム通知:重要なイベント(例:Ticketの期限超過)にSlackで通知。
  • プロセス監視:意図しないイベント(例:未レビューでステータス変更)を検出。

開発環境の準備

第3章の環境を基に、以下の追加準備を行います:

  • Python 3.8以降mcpライブラリrequestsライブラリClaude Desktop:これまでと同じ。
  • python-dotenv:環境変数の管理(既にインストール済み)。
  • ngrok:ローカルサーバーを公開し、Webhookを受信。
  • Jiraプロジェクト:Webhook対応の設定。

インストールコマンド(必要に応じて):

pip install requests python-dotenv

Jiraのセットアップ

  1. Jira APIトークンの確認
    • 第3章のトークンを使用。
    • 権限を確認:プロジェクトでのTicket操作(読み書き)、Webhook管理。
  2. Webhookの設定
    • Jiraインスタンス(例:https://your-domain.atlassian.net)の「System」→「Webhooks」に移動(管理者権限が必要)。
    • 新しいWebhookを追加:
      • Name:例:MCP-Webhook
      • URL:ngrokで生成するURL(例:https://abc123.ngrok.io/webhook)。
      • EventsIssue createdIssue updatedComment createdを選択。
      • JQL filterproject = MCP(プロジェクトMCPに限定)。
      • Status:Enabled。
    • シークレット(オプション):Webhook署名検証用の文字列を設定(例:mysecret)。
  3. プロジェクト準備
    • 第3章のプロジェクト(例:MCP-TEST、キー:MCP)を使用。
    • Ticketやコメントを作成し、イベントを生成(例:Ticketを開く、ステータスを変更)。
  4. ngrokの設定
    • ngrokをインストール(brew install ngrokまたは公式サイト)。
    • ローカルサーバーを公開:
      ngrok http 8135
      
    • 生成されたURLを記録(例:https://abc123.ngrok.io)。
    • JiraのWebhook設定にURLを設定(例:https://abc123.ngrok.io/webhook)。
  5. 環境変数
    第3章の.envファイルに以下を追加:
    JIRA_URL=https://your-domain.atlassian.net
    JIRA_EMAIL=your_email@example.com
    JIRA_API_TOKEN=your_jira_api_token
    JIRA_PROJECT_KEY=MCP
    JIRA_WEBHOOK_SECRET=mysecret
    

コード例:リアルタイム管理用MCPサーバー

以下のMCPサーバーは、JiraのWebhookを介してイベントを検知し、自動コメントやイベント記録の機能を提供します。

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

class JiraRealtimeServer(MCPServer):
    def __init__(self, host, port, url, email, api_token, project_key, webhook_secret):
        super().__init__(host, port)
        self.url = url
        self.email = email
        self.api_token = api_token
        self.project_key = project_key
        self.webhook_secret = webhook_secret
        self.base_url = f"{url}/rest/api/3"
        self.auth = HTTPBasicAuth(email, api_token)
        self.headers = {
            "Accept": "application/json",
            "Content-Type": "application/json"
        }
        self.latest_event = None
        self.register_resource("get_latest_event", self.get_latest_event)
        self.register_tool("add_comment", self.add_comment)
        self.start_webhook_server()

    def verify_webhook_signature(self, body, signature):
        try:
            computed_sig = "sha256=" + hmac.new(
                self.webhook_secret.encode("utf-8"),
                body.encode("utf-8"),
                hashlib.sha256
            ).hexdigest()
            return hmac.compare_digest(computed_sig, signature)
        except Exception as e:
            return False

    def add_comment(self, params):
        try:
            ticket_key = params.get("ticket_key", "")
            comment = params.get("comment", "")
            if not ticket_key or not comment:
                return {"status": "error", "message": "Ticketキーとコメントが必要です"}
            
            url = f"{self.base_url}/issue/{ticket_key}/comment"
            payload = {"body": comment}
            response = requests.post(url, headers=self.headers, auth=self.auth, 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 get_latest_event(self, params):
        try:
            if self.latest_event:
                event_type = self.latest_event.get("event", "")
                event = self.latest_event.get("payload", {})
                event_info = {
                    "event_type": event_type,
                    "action": event.get("webhookEvent", ""),
                    "ticket_key": event.get("issue", {}).get("key", ""),
                    "user": event.get("user", {}).get("displayName", "unknown"),
                    "created_at": event.get("issue", {}).get("fields", {}).get("created", "")
                }
                return {"status": "success", "event_info": event_info}
            return {"status": "success", "event_info": None, "message": "イベントなし"}
        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-Hub-Signature-256", "")

                # Webhook署名検証
                if not self.server.parent.verify_webhook_signature(post_data.decode("utf-8"), signature):
                    self.send_response(401)
                    self.end_headers()
                    self.wfile.write(b"Invalid signature")
                    return

                data = json.loads(post_data.decode("utf-8"))
                event_type = data.get("webhookEvent", "").split(":")[0]

                # イベント処理
                if event_type in ["jira:issue_created", "jira:issue_updated", "comment_created"]:
                    self.server.parent.latest_event = {"event": event_type, "payload": data}

                self.send_response(200)
                self.end_headers()
                self.wfile.write(b"Webhook received")

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

if __name__ == "__main__":
    load_dotenv()
    server = JiraRealtimeServer(
        host="localhost",
        port=8135,
        url=os.getenv("JIRA_URL"),
        email=os.getenv("JIRA_EMAIL"),
        api_token=os.getenv("JIRA_API_TOKEN"),
        project_key=os.getenv("JIRA_PROJECT_KEY"),
        webhook_secret=os.getenv("JIRA_WEBHOOK_SECRET")
    )
    print("JiraリアルタイムMCPサーバーを起動中: http://localhost:8135")
    server.start()

コードの説明

  • verify_webhook_signature:JiraのWebhook署名(X-Hub-Signature-256)をHMAC-SHA256で検証。
  • add_comment:指定したTicketにコメントを追加。Ticketキーとコメント内容を指定。
  • get_latest_event:Webhookで受信した最新イベント(Ticket作成、更新、コメント)を取得。
  • start_webhook_server:ローカルWebhookサーバーを起動し、Jiraからのイベントを処理。
  • WebhookHandler:Jiraのイベント(Issue created、updated、comment created)を受信し、署名検証を行う。

前提条件

  • JiraプロジェクトにWebhookが設定済み(jira:issue_createdjira:issue_updatedcomment_createdイベント)。
  • ngrokでWebhook URLが公開され、Jiraに登録済み。
  • .envファイルに正しいJIRA_URLJIRA_EMAILJIRA_API_TOKENJIRA_PROJECT_KEYJIRA_WEBHOOK_SECRETが設定済み。
  • APIトークンにプロジェクトへの読み書き権限がある。

サーバーのテスト

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

  1. ngrok起動

    ngrok http 8135
    

    ngrok URL(例:https://abc123.ngrok.io)を記録し、JiraのWebhook設定に設定(例:https://abc123.ngrok.io/webhook)。

  2. サーバー起動

    python jira_realtime_server.py
    

    コンソールに「JiraリアルタイムMCPサーバーを起動中: http://localhost:8135」と「Webhookサーバーを起動中: http://localhost:8135」が表示。

  3. イベント検知のテスト

    • JiraのUIでプロジェクトにTicketを作成(例:「バグ報告」)またはステータスを変更。
    • サーバーがイベントをWebhook経由で検知。
  4. 最新イベント取得のテスト
    Pythonでリクエストを送信:

    import requests
    import json
    
    url = "http://localhost:8135"
    payload = {
        "jsonrpc": "2.0",
        "method": "get_latest_event",
        "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",
        "event_info": {
          "event_type": "jira:issue_created",
          "action": "jira:issue_created",
          "ticket_key": "MCP-5",
          "user": "Your Name",
          "created_at": "2025-04-22T14:00:00.000+0000"
        }
      },
      "id": 1
    }
    
  5. 自動コメントのテスト

    payload = {
        "jsonrpc": "2.0",
        "method": "add_comment",
        "params": {
            "ticket_key": "MCP-5",
            "comment": "Ticketを確認しました。詳細を教えてください。"
        },
        "id": 2
    }
    response = requests.post(url, json=payload)
    print(json.dumps(response.json(), indent=2, ensure_ascii=False))
    

    期待されるレスポンス:

    {
      "jsonrpc": "2.0",
      "result": {
        "status": "success",
        "comment_id": "10002"
      },
      "id": 2
    }
    

Claude Desktopとの接続

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

  1. 設定ファイルの編集
    Claude Desktopの設定ファイル(例:claude_desktop_config.json)に以下を追加:

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

    最新のプロジェクトイベントを教えてください。
    

    レスポンス例:

    最新のプロジェクトイベント:
    - イベント:Ticket作成
    - アクション:jira:issue_created
    - Ticketキー:MCP-5
    - ユーザー:Your Name
    - 作成日時:2025-04-22 14:00
    

    別のプロンプト:

    Ticket MCP-5に「Ticketを確認しました。詳細を教えてください。」とコメントしてください。
    

    レスポンス例:

    Ticket MCP-5にコメントを追加しました。
    

実装のコツと注意点

  • Webhook検証:Jiraの署名検証(X-Hub-Signature-256)を正しく処理。
  • レートリミティング:Jira APIの制限(例:600リクエスト/分、クラウドインスタンスによる)に注意。
  • セキュリティ:本番環境では、HTTPSを有効化し、Webhookシークレットを必須化。
  • テスト:テスト用プロジェクトを作成し、本番データに影響を与えない。
  • 拡張性:大量のイベントを処理する場合、キュー(例:Redis、RabbitMQ)を検討。

試してみよう:挑戦課題

以下の機能を追加して、エージェントを強化してみてください:

  • 「バグ」キーワードを含むTicket作成時に「High」優先度を自動付与。
  • ステータス変更イベントを検知し、Slackに通知する機能。
  • イベントデータをJiraに新しいTicketとして記録するツール。

まとめと次のステップ

この第4章では、JiraのWebhookを活用してリアルタイム管理AIを構築しました。Ticketやステータス変更のイベントをリアルタイムで検知し、AIが自動コメントや通知を行えるようになりました。

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


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

1
2
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
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?