Claude CodeでDiscord Botを作る — 公式チャンネルとの違いと実装手順
Claude CodeにDiscordから話しかける公式チャンネルと、Claude Codeで自作するスラッシュコマンドBotの違いを解説。後者のアプリ登録から実装、ngrok公開までを扱います。
Claude CodeでDiscord Botを作る流れ
「Claude DiscordのBot」には2通りの意味があります。DiscordからClaude Codeのセッションを操作したいなら、公式のチャンネル機能(Discordプラグイン)を使います。Claude CodeにコードごとBotを作らせたいなら、この記事の手順1以降が答えです。
Claude Codeはコードベースを読み、ファイルを編集し、コマンドを実行できるターミナル常駐のコーディングエージェントです。Discord Bot開発でも、Expressサーバーの雛形作成からスラッシュコマンドの実装、npm install や ngrok の実行まで、手を動かす部分の大半を任せられます。
この記事では、DiscordのBot構築チュートリアルの流れに沿って、Claude Codeに何を指示し、どこを自分の手で操作するかを分けて示します。ゴールはスラッシュコマンドが実際にDiscord上で応答するところまでです。
DiscordからClaude Codeを操作する公式チャンネル
Claude Codeには、実行中のセッションへ外部のメッセージを届けるチャンネル機能があります。Telegram・Discord・iMessageが研究プレビューとして含まれ、claude.aiのログインかConsoleのAPIキーが必要です。Amazon Bedrock・Google CloudのAgent Platform・Microsoft Foundry経由では使えません。仕組みの全体像はClaude Code Channelsとはにあります。
Discordで使う流れは次のとおりです。Bunのインストールが前提で、TeamとEnterpriseでは組織側の有効化も要ります。
Discordプラグインの導入
- 1
Botを作る
Developer Portalで新規アプリを作り、BotページでReset Tokenを実行してトークンを控えます。Message Content Intentも有効にします。
- 2
サーバーへ招待する
OAuth2 > URL Generatorで
botスコープを選びます。View Channels・Send Messages・Read Message Historyなど、公式が挙げる権限を付けて招待します。 - 3
プラグインを入れて設定する
Claude Codeで次の2つを順に実行します。
/plugin install discord@claude-plugins-official
/discord:configure <token>Claude Codeを終了し、--channels を付けて起動し直します。
claude --channels plugin:discord@claude-plugins-officialBotにDMを送ると、ペアリングコードが返ります。Claude Codeで /discord:access pair <code> を実行し、続けて /discord:access policy allowlist で自分のアカウントだけに絞ります。絞り込みの考え方はChannelsの送信者チェックで扱っています。
注意点は、チャンネルが開いているセッションの間しか届かないことです。Botは --channels で起動したClaude Codeが動いている間だけ応答します。常時稼働させるには、バックグラウンドのプロセスか常駐ターミナルでClaude Codeを動かします。
スラッシュコマンドを自作したい、Botを独立したサーバーとして動かしたいといった場合は、次の節以降のInteractionsエンドポイント方式が向いています。
前提条件
- Node.js(サンプルアプリの
package.jsonは>=18.xを指定しています) - Claude Codeがインストール済み・ログイン済みであること(
claudeコマンドで起動) - Discordアカウントとテスト用サーバー(Botを試すための自分専用サーバーで十分です)
- ngrokアカウント(ローカルサーバーを一時的に公開するために使います)
Claude Codeはネイティブインストーラーで導入でき、macOS・Linux・WSLでは curl -fsSL https://claude.ai/install.sh | bash、Homebrewでは brew install --cask claude-code が使えます。
手順1: 出発点を決めて雛形を用意する
出発点は2通りあります。Discordのチュートリアルは、サンプルアプリのリポジトリ(discord-example-app)をcloneして始める流れです。
雛形の作り方で変わること
サンプルをcloneする
app.js・commands.js・utils.js・game.js・package.json が揃った状態で始まります。register スクリプトと .gitignore(.env を除外済み)も入っているため、Claude Codeには「このコードを読んで、/testを自分のコマンドに置き換えて」と頼む使い方になります。
空のディレクトリから作る
package.json の初期化から署名検証つきのサーバーまでをClaude Codeが書きます。自分で選んだ構成にできる反面、署名検証の置き方など落とし穴の確認はこちらの役目です。
この記事は空のディレクトリから進める前提で書いています。作業ディレクトリを作り、Claude Codeをそこで起動します。
mkdir discord-bot && cd discord-bot
claude起動したら「ExpressでのDiscord Interactionsを受けるサーバーを作りたい。discord-interactionsパッケージを使う」とプロンプトで伝えれば、package.json の初期化から依存パッケージのインストールまで進められます。
プロジェクトの前提は CLAUDE.md に書いておくと、セッションをまたいでも同じ方針で作業してもらえます。CLAUDE.mdはClaude Codeが毎回のセッション開始時に読み込む永続的な指示ファイルです。Discord Botなら、たとえば次のような内容が効きます。
# discord-bot
- Node.js 18以上、ESM(`"type": "module"`)
- 依存は express / discord-interactions / dotenv
- 環境変数名は APP_ID / DISCORD_TOKEN / PUBLIC_KEY(`.env` に置く)
- /interactions のルートに express.json() などのボディパーサーを挟まない
- コマンド定義は commands.js に集め、`npm run register` で登録する環境変数名はサンプルアプリの .env.sample に合わせています。
複数のスラッシュコマンドを段階的に増やしていく設計であれば、cc-sdd のような仕様駆動開発の手順を先に決めてからClaude Codeに実装させる進め方も選択肢になります。手順の型はcc-sddでClaude Codeの仕様駆動開発を実践するで扱っています。
手順2: Claude Codeの許可設定でトークンを守る
Bot Tokenを扱うプロジェクトでは、コマンドの許可とファイル読み取りの制限を先に決めておくと後が楽です。Claude CodeがBashコマンドを実行する前には確認が入り、「Yes, and don't ask again」を選ぶとその許可がgitリポジトリのルートにある .claude/settings.local.json に保存されます。gitリポジトリの外では保存先が変わります。
許可をまとめて書くなら、プロジェクトの .claude/settings.json に permissions を置きます。
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(npm install)"
],
"deny": [
"Read(./.env)"
]
}
}Bash(npm run *) は npm run start や npm run register に一致しますが、npm install には一致しません。そのため npm install は別の行で許可しています。
Read(./.env) のdenyルールは、Claude Codeの組み込みファイルツールと、cat のような読み取りコマンドがBot Tokenを読むのを防ぎます。ただしこのルールは同じパスへのEditとWriteも止めます。EditとWriteの対象になるのは、Editがv2.1.208以降、Writeがv2.1.228以降です。.env の新規作成もClaude CodeのEdit・Writeツールではできなくなるため、ひな形のコピーは自分で行います。
cp .env.sample .env.env.sample を持たない空のディレクトリなら、Claude Codeに .env.sample を作らせ、コピーと値の記入だけを自分でやる分担になります。なお grep -r pattern . のように対象ファイルを名指ししないコマンドや、Node.jsのスクリプトがファイルを開く処理は、このdenyルールの対象外です。すべてのプロセスからの読み取りを止めたいときはサンドボックスを使います。
コマンドラインで一時的に許可するなら、claude --allowedTools "Bash(npm run *)" のようにも渡せます。claude --help には --allowedTools と --disallowedTools が並んでいます。どちらもカンマか空白区切りでツール名を受け取ります(v2.1.289の記載)。
手順3: Discord Developer Portalでアプリを登録する
ここはブラウザ操作が必要なので、Claude Codeでは代行できません。自分の手で進めます。
Developer Portalでの作業
- 1
アプリを作成する
Developer Portalで新規アプリを作成します。作成直後のアプリにはBotユーザーが最初から有効になっています。
- 2
3つの値を控える
General InformationページでApplication IDとPublic Keyを控えます。BotページでReset Tokenを実行し、Bot Tokenも控えます。Tokenは再表示できません。
- 3
導入先を選ぶ
InstallationページのInstallation Contextsで、導入先を選びます。サーバー導入(Guild Install)と個人アカウント導入(User Install)は、どちらか一方でも両方でも構いません。
- 4
スコープを足す
Install Linkで「Discord Provided Link」を選ぶとDefault Install Settingsが現れます。Guild Installには
applications.commandsとbot、User Installにはapplications.commandsを追加します。botを選ぶとPermissionsメニューが現れるので、チュートリアルに沿ってSend Messagesを選びます。 - 5
テストサーバーに追加する
Install LinkのURLをブラウザで開き、「Add to server」でテスト用サーバーに追加します。メンバー一覧にBotが現れれば完了です。
ユーザー導入のアプリはコマンドしか使えないため、メッセージ送信などBotとしての動作を試すならサーバー導入が必要です。コマンドは、アプリを導入したサーバーに表示されます。先にテストサーバーへ追加しておきます。
Discordのチュートリアルは、Botのトークンを「非常にセンシティブ」な値として、共有やバージョン管理への登録を避けるよう求めています。取得した3つの値は .env に入れます。Application IDは APP_ID、Bot Tokenは DISCORD_TOKEN、Public Keyは PUBLIC_KEY です。サンプルの .gitignore は .env と node_modules を除外しているので、空のディレクトリから進める場合も同じ内容にします。
手順4: インタラクションサーバーを実装する
Discordはスラッシュコマンドが実行されると、登録したInteraction Endpoint URLへHTTPリクエストを送ります。受け側のサーバーには2つの役割が要ります。
- Discordからのリクエストであることを検証する(Ed25519署名の検証)
PINGリクエストに応答し、コマンド実行時のペイロードを処理する
discord-interactionsパッケージには、この検証をExpressのルートに組み込めるverifyKeyMiddlewareが用意されています。サンプルの app.js も app.post('/interactions', verifyKeyMiddleware(process.env.PUBLIC_KEY), ...) の形です。Claude Codeに「discord-interactionsのverifyKeyMiddlewareを使ってExpressサーバーを実装して」と頼めば、検証込みの app.js を書いてもらえます。
パッケージのREADMEには、interactionのルートでbody-parserのような他のミドルウェアを使わないよう書かれています。リクエストボディが書き換わると署名の検証に失敗するためです。CLAUDE.md に「ボディパーサーを挟まない」と書いたのはこのためです。
コマンドを処理する部分は、サンプルでは次の形です。
// "test" コマンドを処理する例(サンプルの app.js より)
if (name === 'test') {
return res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
flags: InteractionResponseFlags.IS_COMPONENTS_V2,
components: [
{
type: MessageComponentTypes.TEXT_DISPLAY,
content: `hello world ${getRandomEmoji()}`
}
]
},
});
}InteractionResponseTypeなどはdiscord-interactionsがエクスポートする定数です。getRandomEmoji() は同じサンプルの utils.js にあるヘルパーです。
応答の書き方は、資料によって違います。discord-interactionsのREADMEの例は data: { content: 'Hello world' } と素の content を返します。チュートリアルのサンプルは、IS_COMPONENTS_V2 フラグを立てて TEXT_DISPLAY コンポーネントに文章を入れる形です。Claude Codeに書かせたコードがどちらの形でも、まず /test で動作を見てから拡張します。
サーバーはデフォルトでポート3000を待ち受け、PORT環境変数で変更できます。
手順5: スラッシュコマンドを登録する
コマンド自体は、DiscordのHTTP APIのPUT /applications/<application_id>/commandsエンドポイントを呼んでグローバルコマンドとして登録します。サンプルでは commands.js の末尾にある ALL_COMMANDS が登録対象で、register スクリプト(node commands.js)が呼び出します。
Claude Codeに登録用スクリプトを実装してもらい、実行だけ自分の環境で行う形が扱いやすいです。
npm run register登録が終わると、Discordのサーバー側にスラッシュコマンドが表示されます。この時点ではまだコマンドを実行しても何も起きません。サーバーがDiscordからのリクエストを受け取る経路が、次の手順で作る公開エンドポイントだからです。
手順6: ngrokでエンドポイントを公開する
ローカルの3000番ポートを一時的に公開し、DiscordからのリクエストをApp側のInteraction Endpoint URLに届くようにします。
npm run start別のターミナルでngrokを起動します。
ngrok http 3000表示されたForwardingのURLの末尾に/interactionsを付けます。それをDeveloper PortalのGeneral InformationページにあるInteraction Endpoint URLへ貼り付けて保存します。保存時にDiscordが自動でPINGリクエストを送り、検証が通れば設定完了です。
あとはDiscord上でコマンドを実行します。実装したハンドラーがチャンネルにメッセージを返せば、一連の構築は完了です。
作業ごとの使い分け早見表
Discord Bot構築の各工程を、Claude Codeに任せやすいかどうかで並べると次のとおりです。
| 作業 | Claude Codeへの向き | 理由 |
|---|---|---|
| Expressサーバー・署名検証の実装 | Claude Codeへの向き◎ | 理由discord-interactionsの使い方を調べながらコードを書ける定型作業 |
npm install / ngrokの実行 | Claude Codeへの向き◎ | 理由Bashツールで実行可能(初回は許可プロンプトあり) |
| Developer Portalでのアプリ作成・トークン発行 | Claude Codeへの向き✕ | 理由ブラウザでの操作が必須で代行できない |
.envの作成とトークン記入 | Claude Codeへの向き△ | 理由Read(./.env)をdenyにすると、Claude CodeのEdit・Writeツールでは作成も書き込みもできない |
| スラッシュコマンド登録スクリプトの実行 | Claude Codeへの向き◎ | 理由npm run register相当のコマンドで完結する |
次の一歩: オプションとボタンのあるコマンド
/test が動いたら、チュートリアルの続きにあたる /challenge コマンドが参考になります。7種類から手を選んで遊ぶじゃんけん風のゲームで、スラッシュコマンドのオプション、ボタン、セレクトメニューを一通り使います。
Claude Codeに頼むときに押さえておきたいのは、次の3点です。
- コマンドの
options配列で、ユーザーが選べる項目を定義する - ボタンなどの操作部品は、アクション行(
ACTION_ROW)の中に入れる - 部品の
custom_id(例:accept_button_<ID>)で、ボタンが押されたときのリクエストを見分ける
ボタンが押されると、Discordはtypeが3(MESSAGE_COMPONENT)のリクエストを送ります。サンプルは進行中のゲームをオブジェクトに入れて覚えていますが、チュートリアルは本番のアプリならデータベースを使うよう注意しています。
よくあるつまずき
エンドポイント検証が失敗する
Interaction Endpoint URLを保存した直後に検証エラーになる場合、ngrokとExpressサーバーが同じポートで動いているか、URLを正しくコピーできているかを確認します。そのうえで/interactionsが末尾に付いているかと、PUBLIC_KEYが.envに入っているかも見ます。interactionのルートにexpress.json()のようなボディパーサーが挟まっていると、正しいリクエストでも署名の検証に失敗します。
コマンドは出るのに何も起きない
npm run register でコマンドが表示されても、Interaction Endpoint URLが未設定か、ngrokのトンネルが閉じていると、実行しても応答は返りません。チュートリアルにも、登録直後はコマンドが表示されるだけで、リクエストを受け取る仕組みがなければ何も起きないと書かれています。
ngrokのURLを毎回貼り直している
ngrokの無料アカウントには、変更できないDev Domainが1つ付きます。ngrokのドキュメントによれば、無料プランで公開エンドポイントに使えるのはこの自動割り当てのドメインだけです。ngrok http 3000 --url 'https://' のようにURLを指定すると、アカウントのDev Domainが割り当てられます。同じURLを使えるので、再起動のたびにDeveloper Portalの設定を更新し直す必要はありません。
Claude Codeがコマンド実行のたびに止まる
npm installやngrokのような繰り返すコマンドを毎回確認されると作業が止まりがちです。手順2のpermissions.allowに書いておくか、「Yes, and don't ask again」で許可すれば、以降は聞かれません。
トークンを誤ってコミットしてしまう
.envを.gitignoreに入れ忘れると、Bot Tokenがリポジトリに残ってしまいます。コミット前にgit statusで.envが含まれていないかを確認する習慣が安全です。含めてしまった場合はDeveloper PortalでToken自体をリセットします。
Claude CodeとDiscord以外の連携先
スラッシュコマンドの受け口ができたら、Botの中で外部サービスを呼び出す拡張も考えられます。Claude Codeから他のサービスをMCP経由で扱う実装例として、Auth0 MCPサーバーをClaude Codeで使う設定ガイドやBrowserbase MCPサーバーでクラウドブラウザをClaude Codeから操作するがあります。Botのハンドラー内からこうした外部連携を呼び出す設計にすれば、単純な応答だけでなく実際の作業を代行するコマンドに広げられます。
Slack向けに常駐bot化する実装は、構成が異なるため別の記事Anthropic Agent SDKでSlack常駐botを作るで扱っています。Discordの場合はHTTPのInteractionsエンドポイントを自分で公開する構成、Slackの例はSocket Modeで外部URL不要という違いがあります。Microsoft Teams向けはClaude CodeでTeams Botを作るにあります。
まとめ
Discord Bot構築の大半はClaude Codeに任せられますが、Developer Portalでのアプリ登録とトークン発行は自分の手で行います。トークンを守るなら、Read(./.env) のdenyルールを入れて .env の作成と記入を自分で担う分担にします。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。
- docs.discord.com/developers/quick-start/getting-started
- github.com/discord/discord-example-app
- github.com/discord/discord-interactions-js
- ngrok.com/docs/universal-gateway/domains
- code.claude.com/docs/en/overview
- code.claude.com/docs/en/channels
- code.claude.com/docs/en/memory
- code.claude.com/docs/en/settings
- code.claude.com/docs/en/permissions