JiraでAIを強化する | 第1章：Ticketデータの第一歩

## はじめに

**Jira**は、プロジェクト管理、課題追跡、Agile開発をサポートする業界標準のプラットフォームです。Ticket（課題）、Sprint、ワークフローを管理し、強力なREST APIとWebhookでデータの取得や自動化が可能です。一方、**Model Context Protocol（MCP）**は、AI（例：Claude）を外部データやツールと標準化された方法で接続するプロトコルです。このシリーズでは、JiraとMCPを統合し、Ticketデータを活用するエージェントAIを構築する方法を学びます。たとえば、AIがTicketを分析したり、プロジェクトの進捗を評価したりできます。

第1章では、JiraとMCPの基本を理解し、JiraプロジェクトからTicketやSprintデータを取得するシンプルなMCPサーバーを構築します。Claude Desktopと接続して、プロジェクト管理の第一歩を踏み出しましょう。

## JiraとMCPの概要

### Jiraとは？
Jiraは、以下のような特徴を持つプロジェクト管理プラットフォームです：
- **Ticket管理**：課題（Issue）、バグ、タスクを追跡。
- **強力なAPI**：REST APIでTicket、Sprint、ユーザー、プロジェクトデータにアクセス。
- **Webhookサポート**：リアルタイムイベント（Ticket作成、ステータス変更など）を検知。
- **多用途**：ソフトウェア開発からビジネスプロジェクトまで対応。

### MCPの役割
MCPは、AIにJiraのデータを提供する橋渡し役です。MCPサーバーを通じて、AIは以下を行えます：
- **データ取得**：Ticket、Sprint、コメント情報を取得。
- **アクション実行**：Ticket作成、コメント追加、ステータス更新。
- **分析**：プロジェクトの進捗やチームのパフォーマンスを評価。

### なぜJiraとMCPを組み合わせる？
この組み合わせは、以下のようなシナリオで強力です：
- 自動Ticket管理：AIがバグ報告を分類し、適切な優先度を設定。
- 進捗分析：AIがSprintの完了率や遅延リスクを評価。
- 通知最適化：AIが重要なイベント（例：期限超過）をSlackやメールで通知。

## 開発環境の準備

MCPサーバーとJiraを統合するには、以下の環境を準備します：
- **Python 3.8以降**：サーバー開発用。
- **mcpライブラリ**：JSON-RPC通信を処理（仮定のライブラリ、公式ドキュメントを参照）。
- **requestsライブラリ**：Jira APIとの通信用。
- **Claude Desktop**：MCP対応のAIクライアント。
- **テキストエディタ**：VS Code推奨。

インストールコマンド：
```bash
pip install mcp jsonrpcserver requests python-dotenv
```

### Jiraのセットアップ
1. **Jira APIトークンの作成**：
   - [Atlassian Account](https://id.atlassian.com/manage-profile/security/api-tokens)にアクセスし、APIトークンを生成。
   - トークンを保存（例：`your_jira_api_token`）。
2. **Jiraプロジェクト準備**：
   - テスト用プロジェクト（例：`MCP-TEST`）を作成。
   - Ticketをいくつか追加（例：「バグ修正」「新機能開発」）。
   - Sprintを1つ作成し、Ticketを割り当て。
   - プロジェクトキー（例：`MCP`）とJiraインスタンスURL（例：`https://your-domain.atlassian.net`）を記録。
3. **環境変数の設定**：
   `.env`ファイルに以下を追加：
   ```bash
   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用MCPサーバー

```python
from mcp import MCPServer
import os
from dotenv import load_dotenv
import requests
from requests.auth import HTTPBasicAuth

class JiraServer(MCPServer):
    def __init__(self, host, port, url, email, api_token, project_key):
        super().__init__(host, port)
        self.url = url
        self.email = email
        self.api_token = api_token
        self.project_key = project_key
        self.base_url = f"{url}/rest/api/3"
        self.auth = HTTPBasicAuth(email, api_token)
        self.headers = {"Accept": "application/json"}
        self.register_resource("get_tickets", self.get_tickets)
        self.register_resource("get_sprints", self.get_sprints)

    def get_tickets(self, params):
        try:
            url = f"{self.base_url}/search"
            query = {
                "jql": f"project={self.project_key} AND status={params.get('status', 'Open')}",
                "maxResults": params.get("limit", 10)
            }
            response = requests.get(url, headers=self.headers, auth=self.auth, params=query)
            response.raise_for_status()
            issues = response.json()["issues"]
            ticket_list = [
                {
                    "key": issue["key"],
                    "summary": issue["fields"]["summary"],
                    "status": issue["fields"]["status"]["name"],
                    "creator": issue["fields"]["creator"]["displayName"],
                    "created": issue["fields"]["created"]
                }
                for issue in issues
            ]
            return {"status": "success", "tickets": ticket_list}
        except Exception as e:
            return {"status": "error", "message": str(e)}

    def get_sprints(self, params):
        try:
            url = f"{self.base_url}/project/{self.project_key}/sprint"
            response = requests.get(url, headers=self.headers, auth=self.auth)
            response.raise_for_status()
            sprints = response.json()["values"]
            sprint_list = [
                {
                    "id": sprint["id"],
                    "name": sprint["name"],
                    "state": sprint["state"],
                    "start_date": sprint.get("startDate", ""),
                    "end_date": sprint.get("endDate", "")
                }
                for sprint in sprints
            ]
            return {"status": "success", "sprints": sprint_list}
        except Exception as e:
            return {"status": "error", "message": str(e)}

if __name__ == "__main__":
    load_dotenv()
    server = JiraServer(
        host="localhost",
        port=8132,
        url=os.getenv("JIRA_URL"),
        email=os.getenv("JIRA_EMAIL"),
        api_token=os.getenv("JIRA_API_TOKEN"),
        project_key=os.getenv("JIRA_PROJECT_KEY")
    )
    print("Jira MCPサーバーを起動中: http://localhost:8132")
    server.start()
```

### コードの説明
- **requests**：Jira APIにGETリクエストを送信し、TicketやSprintを取得。
- **get_tickets**：プロジェクトのTicketを取得（キー、サマリー、ステータス、作成者、作成日）。
- **get_sprints**：プロジェクトのSprintを取得（ID、名前、状態、開始日、終了日）。
- **register_resource**：TicketとSprint取得をリソースとして登録。
- **start()**：サーバーを起動。

### 前提条件
- Jira APIトークンが生成済み。
- テスト用プロジェクトにTicketとSprintが存在。
- `.env`ファイルに正しい`JIRA_URL`、`JIRA_EMAIL`、`JIRA_API_TOKEN`、`JIRA_PROJECT_KEY`が設定済み。
- ユーザーにプロジェクトへのアクセス権限がある。

## サーバーのテスト

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

1. **サーバー起動**：
   ```bash
   python jira_server.py
   ```
   コンソールに「Jira MCPサーバーを起動中: http://localhost:8132」と表示。

2. **Ticket取得のテスト**：
   Pythonでリクエストを送信：
   ```python
   import requests
   import json

   url = "http://localhost:8132"
   payload = {
       "jsonrpc": "2.0",
       "method": "get_tickets",
       "params": {"status": "Open", "limit": 5},
       "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",
       "tickets": [
         {
           "key": "MCP-1",
           "summary": "バグ修正",
           "status": "Open",
           "creator": "Your Name",
           "created": "2025-04-22T10:00:00.000+0000"
         },
         {
           "key": "MCP-2",
           "summary": "新機能開発",
           "status": "Open",
           "creator": "Your Name",
           "created": "2025-04-22T11:00:00.000+0000"
         }
       ]
     },
     "id": 1
   }
   ```

3. **Sprint取得のテスト**：
   ```python
   payload = {
       "jsonrpc": "2.0",
       "method": "get_sprints",
       "params": {},
       "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",
       "sprints": [
         {
           "id": 1,
           "name": "Sprint 1",
           "state": "ACTIVE",
           "start_date": "2025-04-20T09:00:00.000+0000",
           "end_date": "2025-05-04T17:00:00.000+0000"
         }
       ]
     },
     "id": 2
   }
   ```

## Claude Desktopとの接続

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

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

2. **Claudeでテスト**：
   Claude Desktopを起動し、プロンプトを入力：
   ```
   プロジェクトのオープンTicketを教えてください。
   ```
   レスポンス例：
   ```
   プロジェクト MCP のオープンTicket：
   - MCP-1: バグ修正（作成者：Your Name、2025-04-22）
   - MCP-2: 新機能開発（作成者：Your Name、2025-04-22）
   ```

   別のプロンプト：
   ```
   現在のSprintを教えてください。
   ```
   レスポンス例：
   ```
   プロジェクト MCP の現在のSprint：
   - Sprint 1（ID: 1、状態：アクティブ、期間：2025-04-20～2025-05-04）
   ```

## 次のステップ

このMCPサーバーは、JiraとAIの連携の基礎です。次の第2章では、JiraにTicketを作成したり、コメントを追加したりする機能を実装し、プロジェクトタスクを自動化するエージェントを構築します。たとえば、AIが特定のキーワードに基づいてTicketに優先度を付けることができます。

## まとめ

この第1章では、JiraとMCPの基本を学び、シンプルなMCPサーバーを構築してTicketとSprintデータを取得しました。Jiraの強力なAPIとMCPの柔軟性を組み合わせることで、AIをプロジェクト管理の強力なアシスタントに変えられます。次章以降では、Ticket管理、プロジェクト分析、リアルタイム管理へと進化させます。

---

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