🎵

ClaudeとCursorから操作できるMP3プレイヤーMCPサーバーを作ってみた

に公開

はじめに

最近、CursorやClaude Desktopで様々なMCPサーバーを使うことが多くなってきました。PostgreSQLやSlackなど、便利なMCPサーバーを活用する中で、「自分でも作ってみたい」という思いが強くなってきました。

しかし、実際に作ったことはなかったので、まずは身近なテーマでサクッと作ってみることにしました。それが、このMP3プレイヤーMCPサーバーです。

ClaudeやCursorから自然言語で音楽を操作できるのは、想像以上に便利で楽しく、MCPサーバー開発の良い入門になりました。

この記事では、実際に作ったMP3プレイヤーMCPサーバーの紹介と、作成過程で学んだことを共有します。

作った経緯

  • MCPサーバーをサクッと作ってみたかった: MCPの仕組みを理解するには、実際に何か作ってみるのが一番早いと思いました
  • ClaudeとCursorから操作してみたかった: 自然言語でローカルのMP3ファイルを再生できたら面白そう、という単純な動機です

MCPサーバーとは?

MCP(Model Context Protocol)は、Anthropic社が提唱する、AI アシスタントがローカルのツールやデータソースと連携するための標準プロトコルです。

簡単に言うと:

  • ClaudeやCursorなどのAIアシスタントに「新しい能力」を追加できる
  • ローカルファイルの操作、データベースへのアクセスなど、様々な機能を提供可能
  • stdio(標準入出力)を使った通信で、実装がシンプル
  • ローカル環境で完結するため、プライベートなデータも安全に扱える

実装した機能

今回作成したMP3プレイヤーは以下の機能を持っています:

基本機能

  • 🎵 MP3ファイルの再生・停止
  • 📋 フォルダー内のMP3ファイル一覧取得
  • 🎲 シャッフル再生
  • 🔄 自動再生(曲が終わったら次の曲へ)

プレイリスト機能

  • ⏭️ 次の曲へスキップ
  • ⏮️ 前の曲へ戻る
  • 🔁 リピートモード
    • 全曲リピート
    • 1曲リピート
    • 順番再生
    • シャッフル

技術スタック

# 主な使用ライブラリ
- mcp: MCPサーバーの実装
- pygame: 音楽再生エンジン
- asyncio: 自動再生の監視

シンプルな構成で、合計400行程度のコードで実装できました。

アーキテクチャ

Claude/Cursor
    ↓ (MCP Protocol via stdio)
MP3 Player MCP Server
    ↓ (pygame.mixer)
Audio Output

MCPサーバーは以下の8つのツールを提供します:

  1. list_mp3_files: MP3ファイル一覧
  2. play_mp3: 特定ファイルの再生
  3. play_all: 全曲再生(シャッフルオプション付き)
  4. stop_playback: 停止
  5. get_playback_status: 再生状態の取得
  6. next_track: 次の曲へ
  7. previous_track: 前の曲へ
  8. set_play_mode: 再生モード設定

MCPサーバーの基本構造

実際のコードは以下のような構造になっています:

from mcp.server import Server
from mcp.types import Tool, TextContent
from mcp.server.stdio import stdio_server

# MCPサーバーのインスタンスを作成
app = Server("mp3-player")

# 利用可能なツールを定義
@app.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="play_mp3",
            description="Play a specific MP3 file",
            inputSchema={
                "type": "object",
                "properties": {
                    "filename": {"type": "string"}
                },
                "required": ["filename"]
            }
        ),
        # ... 他のツールも同様に定義
    ]

# ツールが呼ばれたときの処理
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    if name == "play_mp3":
        result = play_mp3(arguments["filename"])
        return [TextContent(type="text", text=result)]
    # ... 他のツールの処理

# サーバーの起動
async def main():
    async with stdio_server() as (read_stream, write_stream):
        await app.run(
            read_stream,
            write_stream,
            app.create_initialization_options()
        )

if __name__ == "__main__":
    asyncio.run(main())

たった数十行のコードで、AIから呼び出せるツールを作成できます。@app.list_tools()でツールを定義し、@app.call_tool()で実際の処理を実装するだけです。

セットアップ方法

1. 必要なライブラリのインストール

pip install mcp pygame --break-system-packages

2. 設定ファイルの編集

Cursorの場合: ~/.cursor/mcp.json
Claude Desktopの場合:

  • Mac: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "mp3-player": {
      "command": "python3",
      "args": [
        "/path/to/mp3_player_server.py"
      ],
      "env": {
        "MUSIC_FOLDER": "/path/to/your/music"
      }
    }
  }
}

3. アプリケーションの再起動

設定を反映させるため、Cursor/Claude Desktopを完全に終了して再起動します。

使い方

セットアップ後は、自然言語で音楽を操作できます。

MP3ファイル一覧の取得

「音楽再生して」とリクエストすると、まず利用可能なMP3ファイルを確認してくれます:

MP3ファイル一覧

再生と停止

特定の曲を再生したり、停止したりも自然言語で操作できます:

再生と停止

その他の操作例

# 全曲をシャッフル再生
「全部の曲をシャッフルして再生して」

# 次の曲へスキップ
「次の曲にして」

# リピートモードに切り替え
「全曲リピートにして」

# 再生状態を確認
「今何が再生されている?」

実装のポイント

1. 自動再生機能

曲が終わったら自動的に次の曲を再生する機能は、asyncioのタスクで実現しています:

async def auto_play_monitor():
    """Monitor playback and auto-play next track."""
    while True:
        await asyncio.sleep(0.5)
        
        if IS_PLAYING and not pygame.mixer.music.get_busy():
            IS_PLAYING = False
            next_index = get_next_index()
            if next_index is not None:
                play_track_at_index(next_index)

0.5秒ごとに再生状態をチェックし、曲が終わっていたら次の曲を再生します。

2. 柔軟な再生モード

再生モードに応じて次の曲のインデックスを計算する関数を実装:

def get_next_index() -> Optional[int]:
    """Get the next track index based on play mode."""
    if PLAY_MODE == "repeat_one":
        return CURRENT_INDEX
    
    if PLAY_MODE == "shuffle":
        # シャッフル順序に従う
        current_shuffle_pos = SHUFFLE_ORDER.index(CURRENT_INDEX)
        next_shuffle_pos = (current_shuffle_pos + 1) % len(SHUFFLE_ORDER)
        return SHUFFLE_ORDER[next_shuffle_pos]
    
    # Sequential or repeat_all
    next_index = CURRENT_INDEX + 1
    if next_index >= len(PLAYLIST):
        if PLAY_MODE == "repeat_all":
            return 0
        return None
    
    return next_index

3. グローバル状態の管理

MCPサーバーはステートレスな設計が推奨されますが、音楽再生のような継続的な状態を持つ機能では、グローバル変数で状態を管理する必要がありました:

CURRENT_FILE: Optional[str] = None
IS_PLAYING: bool = False
PLAYLIST: list[str] = []
CURRENT_INDEX: int = -1
PLAY_MODE: str = "sequential"

ハマったポイントとデバッグ方法

pygameのインストール問題

開発中、最も時間がかかったのがこの問題でした。

問題の症状

Claude Desktopで以下のエラーが発生:

ModuleNotFoundError: No module named 'pygame'

pygameはインストールしているのに、なぜか認識されない状態でした。

原因

Claude Desktopはpython3コマンドを使ってMCPサーバーを起動しますが、これはシステムのPython/usr/local/bin/python3/usr/bin/python3)を指します。

一方、開発環境でpyenvを使っている場合、pip install pygameでインストールしたpygameはpyenvのPython~/.pyenv/versions/3.11.4/bin/python3)に入っています。

つまり、異なるPythonを見ていることが原因でした。

解決方法

設定ファイルで、pygameがインストールされているPythonの完全なパスを指定します:

{
  "command": "/Users/yourname/.pyenv/versions/3.11.4/bin/python3",
  "args": ["/path/to/mp3_player_server.py"],
  "env": {
    "MUSIC_FOLDER": "/path/to/music"
  }
}

調査手順

この問題に遭遇した場合の調査方法:

# 1. pygameがどこにインストールされているか確認
which python3
# => /usr/local/bin/python3 (システムのPython)

# 2. pyenvのPythonパスを確認
python3 -c "import sys; print(sys.executable)"
# => /Users/yourname/.pyenv/versions/3.11.4/bin/python3

# 3. pygameがインストールされている場所を確認
pip show pygame
# Location: /Users/yourname/.pyenv/versions/3.11.4/lib/python3.11/site-packages

デバッグ方法

MCPサーバーのデバッグには少しコツが必要です。

ログの確認場所

Claude Desktopの場合:

  • Mac: ~/Library/Logs/Claude/mcp-server-mp3-player.log
  • Windows: %APPDATA%\Claude\logs\mcp-server-mp3-player.log

Cursorの場合:

  • DevToolsのコンソール(View → Toggle Developer Tools)

print文でのデバッグ

MCPサーバーはstdio(標準入出力)で通信するため、通常のprint()は使えません:

# ❌ NG: stdoutはMCPの通信に使われるため使えない
print("Debug message")

# ✅ OK: stderrに出力
import sys
print("Debug message", file=sys.stderr, flush=True)

ローカルでのテスト

環境変数を設定して、直接実行することもできます:

MUSIC_FOLDER=/path/to/music python3 mp3_player_server.py

ただし、MCPプロトコルでの通信が必要なため、実際の動作確認にはClaude/Cursorからの実行が必要です。

日本語ファイル名への対応

日本語や絵文字を含むファイル名にも対応するため、Pathオブジェクトを適切に使用しました:

from pathlib import Path

def get_mp3_files() -> list[str]:
    """Get list of MP3 files in the music folder."""
    mp3_files = []
    for file in MUSIC_FOLDER.glob("*.mp3"):
        mp3_files.append(file.name)  # Pathオブジェクトで自動的にエンコーディング処理
    return sorted(mp3_files)

pathlib.Pathを使うことで、プラットフォーム間のパス処理やエンコーディングの違いを吸収できます。

CursorとClaude Desktopでの違い

両方で試してみて分かった違いをまとめます:

項目 Cursor Claude Desktop
設定ファイル ~/.cursor/mcp.json Mac: ~/Library/Application Support/Claude/claude_desktop_config.json / Windows: %APPDATA%\Claude\claude_desktop_config.json
Python環境 開発環境に準拠(VS Codeと同じ) システムのPython(要注意)
デバッグ DevToolsで確認しやすい ログファイルを見る必要あり
再起動速度 速い 遅い(完全終了が必要)
開発体験 ⭐⭐⭐⭐⭐ コード編集とMCP動作確認が同時にできる ⭐⭐⭐ 別アプリなので切り替えが必要
pyenv問題 ほとんど発生しない 頻繁に発生(完全パス指定が必要)

開発中はCursorで試して、完成したらClaude Desktopでも使う、という流れがおすすめです。

実際に使ってみた感想

良かった点

  1. 自然言語での操作が快適: 「次の曲」「シャッフル」など、直感的な指示で操作できる
  2. 作業用BGMに最適: 「全曲リピートで再生して」と言うだけで作業環境が整う
  3. MCPの学習に最適: シンプルな仕様で、MCPの基本を理解しやすい

改善の余地

  1. 音量調整機能がない: 現在は音量をpygame側で固定している
  2. 対応フォーマットがMP3のみ: WAV、OGGなども対応したい
  3. サブフォルダー未対応: 現在は指定フォルダー直下のみ検索

今後の拡張案

  • 音量調整機能の追加
  • WAV、OGG、FLAC対応
  • プレイリストの保存・読込
  • イコライザー機能
  • 再生位置のシーク機能
  • アルバムアート表示(可能であれば)

他にこんなMCPサーバーが作れる

今回のMP3プレイヤーで基本が分かったので、他にも色々なMCPサーバーのアイデアが浮かんできました:

ローカルファイル操作系

  • ドキュメント検索サーバー: 指定フォルダー内のMarkdownやPDFを全文検索
  • 画像処理サーバー: リサイズ、フォーマット変換、EXIF情報取得
  • ログ解析サーバー: アプリケーションログを解析して異常検知

開発ツール連携系

  • Git操作サーバー: ブランチ切り替え、コミット、プッシュを自然言語で
  • データベースサーバー: SQLiteやPostgreSQLへのクエリ実行
  • Docker管理サーバー: コンテナの起動・停止・ログ確認

システム制御系

  • スクリーンショットサーバー: 画面キャプチャとOCR
  • システム監視サーバー: CPU、メモリ、ディスク使用率の監視
  • 通知サーバー: デスクトップ通知やSlackへの投稿

データ処理系

  • CSV分析サーバー: CSVファイルの統計情報や可視化
  • JSON変換サーバー: JSONとYAML、TOMLの相互変換
  • テキスト処理サーバー: 正規表現検索、置換、整形

MCPの良いところは、既存のPythonライブラリをそのまま活用できることです。自分の困りごとに合わせて、気軽にツールを作れます。

まとめ

MCPサーバーを初めて作ってみましたが、思っていたよりも簡単に実装できました。特に:

  • MCPの仕様がシンプル: stdio通信とJSON-RPC形式のメッセージング
  • Pythonのmcpライブラリが便利: デコレーターベースで直感的
  • 実用的なものが数時間で作れる: 今回は構想から実装まで半日程度

ClaudeやCursorから自然言語で操作できる自分専用のツールを作るのは、想像以上に楽しく実用的でした。

皆さんもぜひ、自分の困りごとを解決するMCPサーバーを作ってみてください!

リポジトリ

完全なソースコードはGitHubで公開しています:

実際のコードを見ると、記事で紹介した内容の詳細が分かります。約400行のシンプルな実装なので、MCPサーバー開発の参考にしてください。

参考リンク

リバナレテックブログ

Discussion