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

自作MCP Serverで業務効率化!Redmine連携サーバーを作ってみた

Posted at

はじめに

こんにちは!開発部の天津炒飯です。

Claude DesktopのModel Context Protocol (MCP) を使って、RedmineとClaude間の連携サーバーを自作してみました。MCPサーバーを初めて作る方の参考になれば幸いです。

MCP(Model Context Protocol)は、Claudeが外部システムと連携するための仕組みです。

何を作ったの?

Redmineの操作をClaude経由でできるMCPサーバーを作りました。

できること

  • 📋 プロジェクト一覧・詳細の取得
  • 🎫 チケットの検索・情報の取得
  • 👥 ユーザー情報の取得
  • 📊 ログ出力でデバッグも楽々

実際にClaude Desktopで「今週のタスクを確認して」みたいに話しかけられるようになりました!

プロジェクト構成

シンプルな構成にしました:

redmine-mcp/
├── pyproject.toml          # 依存関係とか
├── README.md
├── .env.example            # 環境変数の例
└── src/
    └── redmine_mcp/
        ├── __init__.py
        └── server.py       # メインファイル

実装のポイント

1. 依存関係で最初にハマった💦

pyproject.toml
[project]
name = "redmine-mcp"
version = "0.1.0"
requires-python = ">=3.10"  # ここ重要!
dependencies = [
    "mcp>=1.0.0",
    "python-redmine>=2.4.0",
    "pydantic>=2.0.0"
]

要注意: MCPはPython 3.10以上じゃないと動かない!最初3.8で設定してハマりました

2. Redmineとの連携部分

python-redmineライブラリを使ってRedmineのAPIを叩きます:

src/redmine_mcp/server.py
from redminelib import Redmine
from redminelib.exceptions import BaseRedmineError

def _connect_to_redmine(self):
    """Redmineに接続"""
    redmine_url = os.getenv("REDMINE_URL")
    redmine_key = os.getenv("REDMINE_KEY") 
    
    self.redmine = Redmine(redmine_url, key=redmine_key)
    # 接続テスト
    current_user = self.redmine.user.get('current')
    print(f"✅ Redmine接続成功: {current_user.firstname}さん")

APIキーは、Redmineの「個人設定」→「APIアクセスキー」から取得できます。

3. ツール定義が一番大事!

MCPサーバーの肝は、Claudeに提供する「ツール」の定義です:

src/redmine_mcp/server.py
@self.server.list_tools()
async def handle_list_tools() -> List[types.Tool]:
    return [
        # プロジェクト一覧取得
        types.Tool(
            name="get_projects",
            description="Redmineプロジェクトの一覧を取得します",
            inputSchema={
                "type": "object",
                "properties": {
                    "limit": {
                        "type": "integer", 
                        "description": "取得する最大件数",
                        "default": 100
                    }
                }
            }
        ),
        # チケット作成
        types.Tool(
            name="create_issue",
            description="新しいチケットを作成します",
            inputSchema={
                "type": "object",
                "properties": {
                    "project_id": {"type": "string", "description": "プロジェクト識別子"},
                    "subject": {"type": "string", "description": "チケットのタイトル"},
                    "description": {"type": "string", "description": "詳細説明"},
                    "tracker_id": {"type": "integer", "description": "トラッカーID"},
                    "assigned_to_id": {"type": "integer", "description": "担当者ID"}
                },
                "required": ["project_id", "subject"]
            }
        )
        # 他のツールも同様に...
    ]

ポイントは:

  • descriptionでClaude側に機能を説明
  • inputSchemaで引数の型と説明を定義
  • 必須パラメータはrequiredで指定

4. ツール実行の処理

定義したツールが呼ばれたときの処理:

src/redmine_mcp/server.py
@self.server.call_tool()
async def handle_call_tool(name: str, arguments: dict) -> List[types.TextContent]:
    """ツール実行時の処理"""
    
    if name == "get_projects":
        return await self._get_projects(arguments)
    elif name == "create_issue":
        return await self._create_issue(arguments)
    # ...

async def _create_issue(self, arguments: dict) -> List[types.TextContent]:
    """チケット作成の実装"""
    issue_data = {
        'project_id': arguments['project_id'],
        'subject': arguments['subject']
    }
    
    # オプション引数があれば追加
    if arguments.get('description'):
        issue_data['description'] = arguments['description']
    
    # Redmine APIでチケット作成
    issue = self.redmine.issue.create(**issue_data)
    
    result = f"""
## ✅ チケット作成完了!

- **チケット番号**: #{issue.id}
- **タイトル**: {issue.subject}
- **プロジェクト**: {issue.project.name}
- **URL**: {redmine_url}/issues/{issue.id}
    """
    
    return [types.TextContent(type="text", text=result)]

設定とデプロイ方法

Claude Desktop設定

まずは設定ファイルを作成:

~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "redmine": {
      "command": "/Users/your-name/redmine-mcp/venv/bin/python",
      "args": ["/Users/your-name/redmine-mcp/src/redmine_mcp/server.py"],
      "env": {
        "REDMINE_URL": "https://your-redmine-server.com",
        "REDMINE_KEY": "your-api-key-here"
      }
    }
  }
}

環境準備とサーバー起動

# プロジェクトのパスに
cd /path/to/your/redmine-mcp

# Python 3.10以上の仮想環境作成
python3.11 -m venv venv
source venv/bin/activate

# 依存関係インストール
pip install -e .

サーバー起動方法

手動で起動して動作確認

# 環境変数設定
export REDMINE_URL="https://your-redmine-server.com"
export REDMINE_KEY="your-api-key"

# サーバー起動
python src/redmine_mcp/server.py

成功すると以下のようなログが出ます:

2025-01-15 10:30:45 - INFO - Redmine接続成功: 天津炒飯さん
2025-01-15 10:30:45 - INFO - MCP Server起動完了

Claude Desktopでの自動起動

上記の設定ファイルを作成後、Claude Desktopを完全に再起動すると自動で連携されます。

設定変更後は必ずClaude Desktopを完全に再起動してください!

開発中にハマったところ

1. "No tools or prompts" エラー

Claude Desktopでこんな表示が出る場合:

  • 設定ファイルのJSONが間違っている
  • パスが間違っている(絶対パス推奨)
  • Claude Desktop再起動してない
# JSON構文チェック
python -m json.tool ~/Library/Application\ Support/Claude/claude_desktop_config.json

2. Python版本エラー地獄

ERROR: Could not find a version that satisfies the requirement mcp>=1.0.0

このエラーが出たら、Python 3.10以上になってるか確認:

python --version
# Python 3.11.x とか出ればOK

# 古い場合はHomebrewでアップデート
brew install python@3.11

実際の使用例

設定完了後、こんな感じに使えます(私はCursor mcpを使用しています):

image.png

普通にしゃべるように操作できるので、めちゃくちゃ楽になりました!

まとめ

MCPサーバー作るの、最初は「難しそう...」と思ってたけど、実際やってみると意外とシンプルでした。

重要だったポイント

  • Python 3.10以上必須 - これで最初に躓く
  • ツール定義が肝 - ここでClaude側の体験が決まる
  • 設定ファイルは絶対パス - 相対パスは罠
  • Claude Desktop再起動必須 - 設定変更したら忘れずに

Redmineだけじゃなく他のツールでも同じ要領で作れそうです。
みなさんも普段使ってるツールでMCPサーバー作ってみませんか?🚀

参考リンク

最後に

はつかぜ株式会社では、IT学習や業務に役立つ情報を定期的にお届けしていきたいと思っています。
システム開発のお問い合わせ・ご相談はこちら:point_down_tone1:

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