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つのツールを提供します:
-
list_mp3_files: MP3ファイル一覧 -
play_mp3: 特定ファイルの再生 -
play_all: 全曲再生(シャッフルオプション付き) -
stop_playback: 停止 -
get_playback_status: 再生状態の取得 -
next_track: 次の曲へ -
previous_track: 前の曲へ -
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ファイルを確認してくれます:

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

その他の操作例
# 全曲をシャッフル再生
「全部の曲をシャッフルして再生して」
# 次の曲へスキップ
「次の曲にして」
# リピートモードに切り替え
「全曲リピートにして」
# 再生状態を確認
「今何が再生されている?」
実装のポイント
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でも使う、という流れがおすすめです。
実際に使ってみた感想
良かった点
- 自然言語での操作が快適: 「次の曲」「シャッフル」など、直感的な指示で操作できる
- 作業用BGMに最適: 「全曲リピートで再生して」と言うだけで作業環境が整う
- MCPの学習に最適: シンプルな仕様で、MCPの基本を理解しやすい
改善の余地
- 音量調整機能がない: 現在は音量をpygame側で固定している
- 対応フォーマットがMP3のみ: WAV、OGGなども対応したい
- サブフォルダー未対応: 現在は指定フォルダー直下のみ検索
今後の拡張案
- 音量調整機能の追加
- 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サーバー開発の参考にしてください。
参考リンク
使い倒せ、テクノロジー。(MAX OUT TECHNOLOGY)をミッションに掲げる、株式会社リバネスナレッジのチャレンジを共有するブログです。Buld in Publichの精神でオープンに綴ります。 Qiita:qiita.com/organizations/leaveanest
Discussion