discord-bot-mcp
# Discord Bot MCP
[](https://github.com/diocata/discord-bot-mcp/actions/workflows/ci.yml)
[](LICENSE)
[](https://nodejs.org/)
A local [Model Context Protocol (MCP)](https://modelcontextprotocol.io/)
server that lets an MCP client operate **your Discord bot** through Discord's
REST API. It provides focused messaging tools, grouped administration tools,
guild scoping, opt-in writes, and secret redaction.
> [!IMPORTANT]
> This is a wrapper around **Discord bot capabilities**. It does not sign in as
> you, impersonate your personal Discord account, or send messages from your
> user identity. Every action is performed by the bot account you create, with
> that bot's name, avatar, roles, and Discord permissions.

## What it can do
The server exposes 20 MCP tools:
| Area | Tools and capabilities |
| --- | --- |
| Everyday use | Get the server, list channels, read/send messages, create threads, add reactions |
| Server structure | Server settings, channels, categories, permission overwrites, roles |
| Community administration | Members, bans, timeouts, scheduled events, AutoMod, emojis, stickers |
| Content administration | Edit/delete/pin messages, threads and membership, invites, webhooks |
| Oversight | Audit-log queries and a guarded raw Discord REST request tool |
See [TOOLS.md](TOOLS.md) for every tool, operation, and safety gate.
## Safety model
- One instance is pinned to one `DISCORD_GUILD_ID`.
- An optional channel allowlist narrows ordinary channel-scoped operations.
- Writes are off unless `DISCORD_ALLOW_WRITES=true`.
- Destructive/admin mutations additionally require
`DISCORD_ALLOW_DESTRUCTIVE_ADMIN=true`.
- Cross-guild raw API paths, full URLs, and traversal are rejected.
- Token-, secret-, and password-like response fields are redacted.
- Outgoing messages disable automatic `@everyone`, role, and user mention parsing.
- Credentials stay in each operator's environment or macOS Keychain.
Guild-wide administration tools are intentionally guild-wide; for example,
listing roles or members is not filtered by a channel allowlist. These controls
supplement Discord permissions rather than replacing them. Give the bot only
the Discord role permissions it actually needs.
## Start here: macOS + Codex
### 1. Install prerequisites
- [Node.js 20 or newer](https://nodejs.org/en/download)
- [pnpm](https://pnpm.io/installation)
- A Discord server where you can add a bot
- An MCP-capable client such as Codex
Check the command-line tools:
```sh
node --version
pnpm --version
```
### 2. Create your Discord application and bot
1. Open the [Discord Developer Portal → Applications](https://discord.com/developers/applications).
2. Select **New Application**, give it a recognizable name, and create it.
3. Open **Bot** in the left sidebar. Discord creates the bot user for the app.
4. Set the bot's username and avatar. This is the identity people will see.
5. Under **Privileged Gateway Intents**, enable only intents your use case
requires. This MCP currently uses the REST API, so most installations do not
need privileged gateway intents.

Useful official references:
- [Discord: Building your first app](https://discord.com/developers/docs/quick-start/getting-started)
- [Discord: Bot users](https://discord.com/developers/docs/topics/oauth2#bot-users)
- [Discord: Gateway intents](https://discord.com/developers/docs/events/gateway#gateway-intents)
### 3. Install the bot in your server
1. In the Developer Portal, open **OAuth2 → URL Generator**.
2. Select the `bot` scope.
3. Select the smallest required bot permissions.
4. Open the generated URL, choose your server, and authorize the bot.
For ordinary use, start with View Channels, Read Message History, Send
Messages, Create Public Threads, and Add Reactions. Add Manage Channels,
Manage Roles, Manage Messages, or other permissions only when you intend to use
those admin operations. **Administrator is convenient for a private test
server but is not the recommended default.**
Review Discord's [permissions reference](https://discord.com/developers/docs/topics/permissions)
before granting administration rights.
### 4. Copy the server ID
In Discord, open **User Settings → Advanced** and enable **Developer Mode**.
Then right-click the server icon and select **Copy Server ID**. Discord calls a
server a *guild* in its API, so this value becomes `DISCORD_GUILD_ID`.
[Discord's official guide to copying IDs](https://support.discord.com/hc/en-us/articles/206346498)
### 5. Store the bot token safely
On the application's **Bot** page, select **Reset Token** and copy the new token
once. Never paste it into a chat, README, issue, Codex configuration, or Git
repository.
On macOS, store it in Keychain without echoing it to the terminal:
```sh
security add-generic-password \
-U \
-s discord-bot-mcp-my-bot \
-a "$USER" \
-w
```
The command prompts for the token. Each teammate uses a different Keychain
entry and preferably their own Discord bot; sharing this repository never
shares a bot identity or token.
If a token is exposed, immediately reset it in the Developer Portal. Deleting
the text later does not make the old token safe.
### 6. Clone and build
```sh
git clone https://github.com/diocata/discord-bot-mcp.git
cd discord-bot-mcp
pnpm install --frozen-lockfile
pnpm verify
```
`pnpm verify` type-checks the code, runs unit and MCP integration tests, and
builds `dist/index.js`.
### 7. Register it with Codex
Find the absolute paths you will use:
```sh
which node
pwd
```
Add the following to `~/.codex/config.toml`, replacing the example paths, guild
ID, Keychain service, and macOS account:
```toml
[mcp_servers.discord]
command = "/absolute/path/from/which/node"
args = ["/absolute/path/to/discord-bot-mcp/dist/index.js"]
[mcp_servers.discord.env]
DISCORD_GUILD_ID = "your_server_id"
DISCORD_KEYCHAIN_SERVICE = "discord-bot-mcp-my-bot"
DISCORD_KEYCHAIN_ACCOUNT = "your-macos-account-name"
DISCORD_ALLOW_WRITES = "false"
DISCORD_ALLOW_DESTRUCTIVE_ADMIN = "false"
DISCORD_ALLOWED_CHANNEL_IDS = ""
```
Restart Codex or open a new task so the MCP tools are discovered. Start in
read-only mode and ask it to list the Discord server and channels. After that
works, opt into ordinary writes:
```toml
DISCORD_ALLOW_WRITES = "true"
```
Only enable destructive administration when you intentionally need it:
```toml
DISCORD_ALLOW_DESTRUCTIVE_ADMIN = "true"
```
### 8. Try it
Example requests to your MCP client:
```text
List the Discord channels you can access.
Read the latest 10 messages in #general.
Send “Hello from my bot” in #bot-testing.
Create a thread called “MVP feedback” in #product.
```
Use a dedicated test channel first. Confirm in Discord that messages show the
bot's name and `BOT` badge—not your personal user identity.
## Other operating systems and clients
On Linux, Windows, containers, or CI, inject `DISCORD_BOT_TOKEN` into the MCP
process through the platform's secret manager. Do not put a real token in a
tracked `.env` file. All other variables are identical.
The server uses standard MCP over stdio and is not tied to Codex. Configure the
same executable and environment variables in any MCP client that supports a
local stdio server.
## Configuration reference
| Variable | Required | Default | Meaning |
| --- | --- | --- | --- |
| `DISCORD_GUILD_ID` | Yes | — | The only Discord server this instance may operate |
| `DISCORD_KEYCHAIN_SERVICE` | macOS option | — | Keychain service containing the token |
| `DISCORD_KEYCHAIN_ACCOUNT` | No | — | Narrows the Keychain lookup to one account |
| `DISCORD_BOT_TOKEN` | Non-Keychain option | — | Bot token supplied by the process environment |
| `DISCORD_ALLOW_WRITES` | No | `false` | Enables messaging and other ordinary mutations |
| `DISCORD_ALLOW_DESTRUCTIVE_ADMIN` | No | `false` | Enables admin/destructive and raw non-GET operations |
| `DISCORD_ALLOWED_CHANNEL_IDS` | No | all guild channels | Comma-separated IDs for ordinary channel-scoped tools |
If `DISCORD_KEYCHAIN_SERVICE` is set, the Keychain token takes precedence over
`DISCORD_BOT_TOKEN`.
## Development and tests
```sh
pnpm check # TypeScript without emitting files
pnpm test:unit # HTTP behavior and security boundaries
pnpm test:integration # Real MCP client/server protocol interaction
pnpm test:live # Optional read-only test against your configured bot
pnpm build # Production JavaScript and declarations
pnpm verify # All gates above
```
The integration suite uses in-memory MCP transports and a mock Discord API. It
does not require a real token and never sends a live Discord message. CI runs
the complete verification matrix on Node.js 20, 22, and 24.
`pnpm test:live` is an optional, read-only smoke test. It requires the same
Discord environment variables as the MCP server and verifies the configured
bot can retrieve exactly one guild and its allowed channels. It forcibly
disables writes regardless of the supplied write flags.
## Troubleshooting
- **`DISCORD_GUILD_ID is required`** — add the server ID to the MCP environment.
- **No token was found in macOS Keychain** — make the service and account match
the `security add-generic-password` command exactly.
- **`Missing Access` / `Missing Permissions`** — give the bot's Discord role the
specific permission needed and ensure its role is high enough in the role list.
- **Writes are disabled** — set `DISCORD_ALLOW_WRITES=true` and restart the MCP client.
- **Destructive administration is disabled** — enable its separate flag only
for the operation, then turn it off again.
- **Tools do not appear** — run `pnpm build`, verify both absolute paths, and
restart the MCP client.
## License
[MIT](LICENSE)
TDQS
Scored across 20 tools
Most tools have clearly distinct purposes, but there is overlap between basic interaction tools and admin tools, e.g., discord_list_channels vs discord_admin_channels both list channels, and discord_read_messages vs discord_admin_messages both read messages. The admin prefix helps, but an agent might still misselect when a simple action could be done by either.
The naming is largely consistent: basic tools use discord_verb_noun (e.g., discord_list_channels, discord_send_message), while admin tools uniformly use discord_admin_resource (e.g., discord_admin_roles, discord_admin_webhooks). The main inconsistency is discord_get_server vs discord_admin_server, and 'read' vs 'list' verbs, but the overall pattern is predictable.
20 tools is borderline heavy. The count is justified by the broad admin scope covering many Discord resources, but it feels somewhat large for a single MCP server. The basic interaction tools are only 5, while the admin tools take up 15, making the set feel imbalanced.
The tool set covers a comprehensive range of Discord server operations: channels, roles, members, messages, threads, events, automod, emojis, stickers, webhooks, invites, and audit logs. Basic bot interactions (send, read, react, thread) are included, and the raw admin request tool fills any remaining gaps, leaving no obvious dead ends.