0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Claude Code】Discord の Channels 機能を拡張する自作 MCP サーバーを作ってみた

0
Last updated at Posted at 2026-06-22

はじめに

前回の記事で、Claude Code の Channels 機能を使って Discord から Claude Code を操作できるようにしました。

そのとき「今後の展望」として書いていたのですが、公式の Discord プラグインはメッセージのやり取りしかできず、チャンネルの作成や削除といったサーバー管理系の操作には対応していません。

学習用の Discord サーバーを Claude Code に整備してもらいたかったので、チャンネル管理ができる MCP サーバーを自作してみました。この記事ではその実装と、途中でハマったポイントを共有します。

前提環境

  • macOS Sequoia
  • Claude Code v2.1.167
  • Python 3.12 / uv 0.9.24
  • Discord Bot(前回記事でセットアップ済み)

公式プラグインでできること

まず、公式プラグイン(discord@claude-plugins-official)で使えるツールを整理しておきます。

ツール 操作
fetch_messages メッセージ取得(最大 100 件)
reply メッセージ送信(ファイル添付可)
react リアクション追加
edit_message 送信済みメッセージの編集
download_attachment 添付ファイルのダウンロード

すべてメッセージ操作です。チャンネルの作成・削除・名前変更やカテゴリの管理といったサーバー管理系の操作は一切ありません。

discord.py ではなく httpx にした理由

Python で Discord Bot を作るなら discord.py が定番ですが、今回はあえて httpx で Discord REST API を直接呼ぶ方針にしました。

discord.py は常駐型の Bot 向けライブラリで、Bot.run() を呼ぶと独自の非同期イベントループに入ります。一方、MCP サーバーは FastMCP が内部で独自のイベントループを持っているため、両者を同居させると競合してうまく動かない、もしくは複雑な実装になりえます。

httpx は独自のイベントループを持たない非同期 HTTP クライアントなので、FastMCP のループの中から await ひとつで Discord の REST API を呼べます。

最終的に採用した技術スタックは以下の通りです。

コンポーネント 技術
言語 Python 3.12(3.10 以上で動作)
MCP SDK mcp v1.27.2(FastMCP)
HTTP クライアント httpx
パッケージ管理 uv

実装

プロジェクト構成

最終的にはこうなりました。

original-tools/
├── pyproject.toml
├── .env                  # Bot Token & Guild ID(サーバーID)
└── src/
    └── discord_mcp/
        ├── __init__.py
        ├── __main__.py   # エントリポイント
        ├── server.py     # MCP サーバー本体
        ├── client.py     # Discord REST API クライアント
        └── tools/
            ├── __init__.py
            └── channels.py   # チャンネル管理ツール

Discord REST API クライアント

Discord REST API v10 を httpx で叩くクライアントです。認証は Authorization: Bot {トークン} ヘッダーを付けるだけです。

# client.py
BASE_URL = "https://discord.com/api/v10"

class DiscordClient:
    def __init__(self, bot_token: str, guild_id: str) -> None:
        self.guild_id = guild_id
        self._headers = {"Authorization": f"Bot {bot_token}"}
        self._http = httpx.AsyncClient(
            base_url=BASE_URL, headers=self._headers, timeout=10.0
        )

    async def _request(self, method: str, path: str, **kwargs: Any) -> Any:
        response = await self._http.request(method, path, **kwargs)

        response.raise_for_status()
        if response.status_code == 204:
            return None
        return response.json()

各操作のメソッドは、この _request を使って対応する REST API エンドポイントを呼ぶだけです。例えばチャンネル作成なら POST /guilds/{guild_id}/channels、削除なら DELETE /channels/{channel_id} という具合で、パターンは同じなので省略します。

MCP ツールの定義

FastMCP の @mcp.tool() デコレータを付けた関数がそのまま Claude Code のツールになります。

# tools/channels.py
@mcp.tool()
async def list_channels() -> str:
    """サーバーのチャンネル一覧を取得する"""
    channels = await client.list_channels()
    # ... 整形して返す

同じパターンで create_channeldelete_channeledit_channelcreate_category の計 5 つを定義しました。

access.json の自動更新

公式の Discord プラグインは ~/.claude/channels/discord/access.json でチャンネルごとのアクセス制御を管理しています。新しいチャンネルを作っても、このファイルにチャンネル ID を追加しないと Bot が反応できません。毎回手動で追加するのは面倒なので、チャンネル作成・削除時に自動で更新するようにしました。

# tools/channels.py
ACCESS_JSON = Path.home() / ".claude" / "channels" / "discord" / "access.json"

def _add_channel_to_access(channel_id: str) -> None:
    data = _read_access()
    groups = data.setdefault("groups", {})
    if channel_id not in groups:
        allow_from = data.get("allowFrom", [])
        groups[channel_id] = {"requireMention": True, "allowFrom": allow_from}
        _write_access(data)

def _remove_channel_from_access(channel_id: str) -> None:
    data = _read_access()
    groups = data.get("groups", {})
    if channel_id in groups:
        del groups[channel_id]
        _write_access(data)

ここで気をつけたのは削除時の順序です。access.json から先にエントリを消してから Discord のチャンネルを削除するようにしています。

逆の順序にすると、Discord 側の削除は成功したのに access.json の更新に失敗した場合、存在しないチャンネルがアクセスリストに残り続けてしまいます。

MCP サーバーのエントリポイント

Bot Token と Guild ID は .env から読み込みます。

# .env
DISCORD_BOT_TOKEN=your_bot_token_here
DISCORD_GUILD_ID=123456789012345678   # サーバーを右クリック →「サーバーIDをコピー」で取得

セットアップ

Bot の権限追加

前回のセットアップではメッセージ関連の権限のみ付けていました。チャンネル管理を行うには、追加で Manage ChannelsView Channels の権限が必要です。

Discord Developer Portal の OAuth2 → URL Generator で権限を追加した URL を生成し、Bot を再招待すると権限が更新されます。

Claude Code への登録

以下のコマンドで MCP サーバーを Claude Code に登録します。

claude mcp add --transport stdio --scope user discord-server-admin -- \
  uv run --directory /path/to/original-tools python -m discord_mcp.server

権限の自動許可

~/.claude/settings.jsonpermissions.allow に以下を追加すると、チャンネル削除以外のツールは確認なしで実行されます。

{
  "permissions": {
    "allow": [
      "mcp__discord-server-admin__list_channels",
      "mcp__discord-server-admin__create_channel",
      "mcp__discord-server-admin__edit_channel",
      "mcp__discord-server-admin__create_category"
    ]
  }
}

削除だけは毎回確認が出るようにして、うっかり消してしまうのを防いでいます。

動作確認

Discord の Channels 経由で Claude Code にチャンネルの作成と削除を依頼してみました。

チャンネルの作成・削除ともに問題なく動作しています。access.json の自動更新が効いているので、作成したチャンネルでそのまま Bot とやり取りできます。

ハマったポイント

--scope user の指定漏れ

claude mcp add のデフォルトはプロジェクトスコープ(カレントディレクトリに紐づく設定)です。

スコープ 指定方法 有効範囲
プロジェクト(デフォルト) claude mcp add 登録したディレクトリのみ
ユーザー claude mcp add --scope user どのディレクトリからでも

私は MCP サーバーのソースがあるディレクトリで claude mcp add を実行してしまい、普段使っている ~/Desktop/work から MCP サーバーが見えなくなっていました。どこからでも使いたい場合は --scope user が必要です。

settings.json.claude.json の混同

~/.claude/settings.json にも mcpServers というキーを書けますが、MCP サーバーの登録先としては機能しません。

ファイル 用途
~/.claude.json MCP サーバーの登録先(claude mcp add が書き込む先)
~/.claude/settings.json 権限設定など(mcpServers を書いても認識されない)

claude mcp add コマンドを使うか、~/.claude.json に直接書き込む必要があります。

おわりに

ということで、公式プラグインにない機能を自作 MCP サーバーでサクッと補完してみました。(Claude Code さまさま)

今回はチャンネル管理だけですが、同じ仕組みでロール管理やメッセージの一括削除なども追加できます。Discord REST API のエンドポイントに対応するツール関数を書くだけなので、必要になったら拡張していこうと思います。

ありがとうございました。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?