@goul4rt/mcp-discord
v0.2.0
Published
The most complete open-source MCP server for Discord — 80+ tools, dual-mode: integrated (plugin) or standalone, with host-extensible tool registry
Maintainers
Readme
mcp-discord
The most complete open-source MCP server for Discord.
Give any MCP client (Claude, Cursor, custom agents) full control over Discord — messages, moderation, channels, roles, and more. Born from production use at delfus.app, open-sourced for the community.
Table of Contents
- Why mcp-discord?
- Getting Started (for MCP/Claude Users)
- Getting Started (for Bot Developers)
- Usage Examples
- Tools
- Troubleshooting
- Architecture
- Integration Guide
- Configuration
Why mcp-discord? {#why-mcp-discord}
- 30+ tools across 8 categories — servers, channels, messages, reactions, members, roles, moderation, and monitoring
- Dual-mode — run standalone (own process) or integrate as a plugin into your existing discord.js bot
- REST-only or Gateway — choose between lightweight REST-only mode or full WebSocket gateway for real-time features
- Two transports — stdio (default, for Claude Desktop / Claude Code) or HTTP with Bearer token auth
- Production-proven — built and used in production at delfus.app
Getting Started: For MCP/Claude Users {#getting-started-mcp}
Prerequisites
- Node.js 18 or higher
- A Discord bot token
Installation
Install via npm:
npm install @goul4rt/mcp-discordConfigure Claude Desktop / Claude Code
Add to your MCP config (usually ~/Library/Application\ Support/Claude/claude_desktop_config.json on Mac):
{
"mcpServers": {
"discord": {
"command": "node",
"args": ["-e", "require('@goul4rt/mcp-discord').runStandalone()"],
"env": {
"DISCORD_TOKEN": "your-bot-token-here"
}
}
}
}First Command
Restart Claude, then ask:
"List all Discord servers I have access to"
Claude will automatically invoke the list_servers tool and show you the results.
With Real-Time Features (Optional)
To enable real-time Discord gateway features (like monitoring new messages as they arrive), add this to your config:
{
"mcpServers": {
"discord": {
"command": "node",
"args": ["-e", "require('@goul4rt/mcp-discord').runStandalone()"],
"env": {
"DISCORD_TOKEN": "your-bot-token-here",
"DISCORD_USE_GATEWAY": "true"
}
}
}
}Getting Started: For Bot Developers {#getting-started-bot}
Prerequisites
- Node.js 18 or higher
- A Discord bot token
- Existing discord.js bot (or create one from scratch)
Clone & Install
# Clone the repository
git clone https://github.com/goul4rt/mcp-discord.git
cd mcp-discord
# Install dependencies
npm install
# Build
npm run buildConfigure
Copy the example environment file:
cp .env.example .envEdit .env and add your Discord bot token:
DISCORD_TOKEN=your-bot-token-hereRun Standalone (Separate Process)
Run the MCP server as a separate process (stdio transport):
npm startOr HTTP transport (for remote clients):
npm run start:httpIntegrate Into Your Existing Bot
Use IntegratedProvider to embed mcp-discord into your existing discord.js bot:
import { IntegratedProvider, createMcpServer } from '@goul4rt/mcp-discord';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const provider = new IntegratedProvider({ client: myDiscordClient });
await provider.connect();
const server = createMcpServer({ provider });
const transport = new StdioServerTransport();
await server.connect(transport);Usage Examples {#usage-examples}
For Claude/MCP Users
Ask Claude naturally — it will automatically invoke the right tool:
Example 1: Send a message
> "Send a message to the #general channel saying 'Hello everyone!'"
Claude invokes: `send_message` with `channelId`, `content`Example 2: Search for a member
> "Find all members in my server with 'admin' in their username"
Claude invokes: `search_members` with `username` filterExample 3: Moderate a user
> "Timeout the user @Spam for 24 hours with reason 'Spam'"
Claude invokes: `timeout_user` with userId, duration, reasonExample 4: View audit log
> "Show me the last 10 bans in the server"
Claude invokes: `get_audit_log` with action filterFor Bot Developers
Use these examples as a starting point for your own MCP server or bot integration:
Example 1: Send a message
import { IntegratedProvider } from '@goul4rt/mcp-discord';
const provider = new IntegratedProvider({ client: myBot });
await provider.connect();
const result = await provider.sendMessage(channelId, {
content: 'Hello @members! New announcement:',
embeds: [{ title: 'Update', description: 'System online' }]
});Example 2: Search members
const members = await provider.searchMembers(serverId, {
username: 'admin'
});
console.log(\`Found \${members.length} members matching search\`);
members.forEach(m => console.log(\`\${m.user.username} - roles: \${m.roles.length}\`));Example 3: Moderate a user
await provider.timeoutUser(serverId, userId, {
duration: 24 * 60 * 60 * 1000, // 24 hours in ms
reason: 'Spam',
moderatorId: botId
});Example 4: Fetch audit log
const auditLog = await provider.getAuditLog(serverId, {
limit: 10,
actionType: 'BAN' // Filter by specific action
});
auditLog.entries.forEach(entry => {
console.log(\`\${entry.action}: \${entry.targetId} by \${entry.executorId}\`);
});Example 5: Create a role
const newRole = await provider.createRole(serverId, {
name: 'Muted',
color: 0xFF0000, // Red
mentionable: false
});
console.log(\`Created role: \${newRole.name}\`);Example 6: Create a thread
const thread = await provider.createThread(channelId, {
name: 'Bug: Login fails on Safari',
messageId: originalMessageId,
autoArchiveDuration: 1440 // 1 day
});Example 7: List all roles
const roles = await provider.listRoles(serverId);
roles.forEach(role => {
console.log(\`\${role.name} - members: \${role.memberCount}\`);
});Example 8: Add a reaction
await provider.addReaction(channelId, messageId, {
emoji: '👍' // Unicode emoji
});
// Or custom emoji:
await provider.addReaction(channelId, messageId, {
emoji: '<:myemoji:123456789>'
});Tools {#tools}
Server / Guild (2 tools)
| Tool | Description |
|------|-------------|
| list_servers | List all Discord servers the bot has access to |
| get_server_info | Get detailed info about a specific server |
Channels (7 tools)
| Tool | Description |
|------|-------------|
| get_channels | List all channels in a server |
| get_channel | Get detailed info about a channel |
| create_channel | Create text, voice, category, announcement, forum, or stage channels |
| edit_channel | Edit channel name, topic, NSFW, slowmode, position, category |
| delete_channel | Permanently delete a channel |
| create_thread | Create a thread in a channel (optionally from a message) |
| archive_thread | Archive a thread |
Messages (8 tools)
| Tool | Description |
|------|-------------|
| send_message | Send messages with text, rich embeds, and replies |
| read_messages | Read recent messages with pagination (up to 100) |
| search_messages | Search messages by content, author, or channel |
| edit_message | Edit a bot message |
| delete_message | Delete a single message |
| delete_messages_bulk | Bulk delete 2-100 messages (< 14 days old) |
| pin_message | Pin a message |
| unpin_message | Unpin a message |
Reactions (2 tools)
| Tool | Description |
|------|-------------|
| add_reaction | Add emoji reaction (Unicode or custom) |
| remove_reaction | Remove a reaction |
Members / Users (4 tools)
| Tool | Description |
|------|-------------|
| list_members | List server members with pagination |
| get_member | Get detailed member info (roles, nickname, join date) |
| get_user | Get info about any Discord user by ID |
| search_members | Search members by username or nickname |
Roles (4 tools)
| Tool | Description |
|------|-------------|
| list_roles | List all roles with permissions, colors, and member counts |
| create_role | Create a new role |
| add_role | Add a role to a member |
| remove_role | Remove a role from a member |
Moderation (4 tools)
| Tool | Description |
|------|-------------|
| timeout_user | Temporarily mute a user (up to 28 days) |
| kick_user | Kick a user from the server |
| ban_user | Ban a user with optional message deletion |
| unban_user | Unban a user |
Monitoring (2 tools)
| Tool | Description |
|------|-------------|
| get_audit_log | View server audit log (bans, kicks, changes) |
| check_mentions | Find recent @mentions of the bot or a user |
Troubleshooting {#troubleshooting}
Discord Token Not Working
Symptoms: "Invalid token" error or 401 Unauthorized from Discord API
Solution:
- Verify you copied the token (not the client secret) from Discord Developer Portal
- Ensure the token hasn't been regenerated (regenerating old token invalidates it)
- Check
.envfile has exact format:DISCORD_TOKEN=token-here(no quotes) - Verify bot has at least one permission configured in Developer Portal → OAuth2 → Scopes
Can't See Commands in Claude
Symptoms: Claude says "I don't have access to Discord tools" or tools don't appear
Solution:
- Ensure the MCP server is running:
- For stdio:
npm startshould show "MCP server listening" - For HTTP:
npm run start:httpshould show "HTTP server on port 3100"
- For stdio:
- Restart Claude/Claude Code app after adding the config
- Check config file path:
- Mac:
~/Library/Application\ Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
- Mac:
- Verify
nodecommand is available in your PATH (typenode --versionin terminal)
Gateway Connection Timeout (Real-Time Features)
Symptoms: When using DISCORD_USE_GATEWAY=true, connection hangs or times out
Solution:
- Check your firewall allows WebSocket connections to Discord:
# Test connectivity to Discord gateway curl -I https://gateway.discord.gg - Verify bot has GATEWAY intents enabled in Discord Developer Portal:
- Go to Applications → Your Bot → Bot → Privileged Gateway Intents
- Enable: Message Content Intent, Server Members Intent
- Try without gateway first (
DISCORD_USE_GATEWAY=false) to isolate the issue
"Bot Lacks Permissions" for Tool
Symptoms: Tool executes but fails with "Missing Permissions: SEND_MESSAGES" (or similar)
Solution:
- Check role hierarchy: Bot role must be higher than the role/member it's trying to manage
- Go to Server Settings → Roles
- Ensure @bot role is above target role
- Verify the bot's role has required permissions:
- Go to Server Settings → Roles → @bot role
- Enable the permission in question (e.g., "Send Messages", "Manage Messages")
- If permission is channel-specific, check Channel Settings → Permissions
- Verify bot role isn't being denied the permission
Architecture {#architecture}
┌─────────────────────────────────────────────────┐
│ MCP Client │
│ (Claude, Cursor, custom) │
└──────────────────┬──────────────────────────────┘
│ stdio or HTTP
┌──────────────────▼──────────────────────────────┐
│ MCP Server │
│ (transport + tool routing) │
└──────────────────┬──────────────────────────────┘
│
┌──────────────────▼──────────────────────────────┐
│ Tool Registry │
│ (30+ tools, Zod validation) │
└──────────────────┬──────────────────────────────┘
│
┌──────────────────▼──────────────────────────────┐
│ DiscordProvider (interface) │
├─────────────────────┬───────────────────────────┤
│ StandaloneProvider │ IntegratedProvider │
│ (own token + REST/ │ (uses host bot's │
│ optional gateway) │ existing connection) │
└─────────────────────┴───────────────────────────┘
│
┌──────────────────▼──────────────────────────────┐
│ Discord API │
└─────────────────────────────────────────────────┘Provider abstraction: MCP tools never touch discord.js directly. They call the DiscordProvider interface, which has two implementations:
- StandaloneProvider — creates its own connection using a bot token. REST-first with optional gateway. Use when running as a separate process.
- IntegratedProvider — receives an existing discord.js
Clientfrom the host bot. Zero overhead, shared cache and gateway. Use when embedding in an existing bot.
Integration Guide {#integration-guide}
To use mcp-discord as a plugin inside your existing discord.js bot:
import { IntegratedProvider, createMcpServer } from '@goul4rt/mcp-discord';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
// Your existing discord.js client
const provider = new IntegratedProvider({ client: myDiscordClient });
await provider.connect();
const server = createMcpServer({ provider });
const transport = new StdioServerTransport();
await server.connect(transport);The IntegratedProvider uses your bot's existing gateway connection — no extra WebSocket, no extra authentication, no extra memory.
Configuration Reference {#configuration-reference}
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| DISCORD_TOKEN | Yes | — | Discord bot token |
| DISCORD_USE_GATEWAY | No | false | Connect to Discord WebSocket gateway for real-time features |
| MCP_TRANSPORT | No | stdio | Transport mode: stdio or http |
| MCP_PORT | No | 3100 | HTTP server port (only when MCP_TRANSPORT=http) |
| MCP_AUTH_TOKEN | No | — | Bearer token for HTTP transport authentication |
Contributing
Contributions are welcome! See CONTRIBUTING.md for development setup, code style, and how to add new tools.
License
Author
Created by @goul4rt. Born from delfus.app, open-sourced for the community.
