How to configure a Claude-Code Discord bot to operate across multiple channels and servers via access.json.
Every new Discord channel = manual update of every bot's access.json. With N bots × M channels growing fast, this becomes unsustainable.
~/.claude/channels/<bot-name>/access.json
{
"dmPolicy": "allowlist",
"allowFrom": ["<nat-user-id>"],
"groups": {
"<channel-id>": {
"requireMention": true,
"allowFrom": ["<user-id-1>", "<user-id-2>"]
}
},
"pending": {}
}| Field | Effect |
|---|---|
dmPolicy: "allowlist" |
Only listed users can DM the bot |
dmPolicy: "pairing" |
Anyone can DM to request pairing |
groups.<channelId> |
Channel-level access rules |
requireMention: true |
Bot only responds when @mentioned |
requireMention: false |
Bot responds to all messages from allowFrom |
allowFrom: [...] |
User IDs allowed to message bot in channel |
*-tagonly → requireMention: true
*-private → requireMention: true
*-dev → requireMention: false
default → requireMention: false
User ID:
Discord → User Settings → Advanced → Developer Mode ON
Then right-click any user → Copy User ID
Channel ID:
Right-click any channel → Copy Channel ID
Guild ID:
Right-click server icon → Copy Server ID
access.json hot-reloads on every incoming message. No bot restart needed when:
- Adding a new channel to
groups - Adding a user to
allowFrom - Toggling
requireMention
{
"dmPolicy": "allowlist",
"allowFrom": ["691531480689541170"],
"groups": {
"1500510701519634545": {
"requireMention": true,
"allowFrom": ["691531480689541170"]
},
"1500682214571118624": {
"requireMention": false,
"allowFrom": ["691531480689541170"]
},
"1500810831309570169": {
"requireMention": true,
"allowFrom": ["691531480689541170"]
},
"1500775333283237970": {
"requireMention": false,
"allowFrom": [
"691531480689541170",
"910909378876571658",
"1488826547485020200"
]
}
},
"pending": {}
}- Don't hardcode bot client ID in CLAUDE.md examples without "this is example, replace with yours"
- Don't use
~inDISCORD_STATE_DIRenv var (tilde doesn't expand in subprocesses) — use$HOMEor absolute path - Don't reuse private channel ID across guilds — channel IDs are globally unique
- Don't forget Message Content Intent in Discord Developer Portal (CRITICAL — bot won't receive messages otherwise)
Build a CLI that:
discord-access.ts auto-sync --guild=<guild-id>Steps:
- Fetch all channels from Discord REST API:
GET /guilds/{guild_id}/channels - For each bot in
~/.claude/channels/*/:- Read current
access.json - For each missing channel: add with default policy (using naming convention)
- Write back
- Read current
Daily cron:
0 * * * * bun discord-access.ts auto-sync --all-guildsHot-reload picks up changes on next message — no restart.
https://discord.com/oauth2/authorize?client_id=<BOT_CLIENT_ID>&scope=bot&permissions=<PERM>&guild_id=<GUILD_ID>
Permission integers:
8= Administrator (full)101392= standard bot (View, Send, Read History, Attach, React, Manage Channels)101376= standard minus Manage Channels
Recipe in active production by Métis Oracle on 2026-05-04 across:
- 2 guilds (LEARN+Monetize, nat's ARRA Oracles)
- 4 channels (kk-workshop, road-to-dev, oracle-lounge, oracle-lounge-tagonly)
- Hot-reload tested — works without restart