@tensakulabs/discord-mcp
v0.1.9
Published
Discord selfbot MCP server — read & send messages via Claude
Readme
discord-mcp
Discord selfbot MCP server — read & send Discord messages from Claude Code or OpenClaw.
⚠️ Selfbot Warning: This uses your Discord user token, which violates Discord's ToS. Your account may be banned. Use at your own risk.
What it does
Exposes 6 Discord tools via MCP (Model Context Protocol):
| Tool | Description |
|------|-------------|
| discord_list_guilds | List all servers you're in |
| discord_list_channels | List text channels in a server |
| discord_get_messages | Fetch recent messages from a channel |
| discord_get_dms | List your open DM conversations |
| discord_send_message | Send a message (channel or DM) |
| discord_get_unread | Get messages you haven't seen yet |
A background daemon maintains a persistent WebSocket to the Discord Gateway, ingesting messages into a local SQLite database so discord_get_unread works even while Claude isn't running.
Setup
1. Install & configure
npx @tensakulabs/discord-mcp setupThis will:
- Show you how to extract your Discord token from the desktop app
- Save it securely to your OS keychain (macOS: uses built-in
securityCLI — no native module required) - Start the background daemon via launchd (macOS) or systemd (Linux)
- Auto-register the MCP server in Claude's config
2. Token extraction (step shown during setup)
Open Discord desktop app → Press Ctrl+Shift+I (or Cmd+Option+I on Mac) → Network tab → Send any message in Discord → filter requests by messages → click any request → Headers tab → find the Authorization header → copy its value.
3. Restart Claude
Restart Claude Code. You'll see Discord tools available.
Verify it's working
npx @tensakulabs/discord-mcp status
# ✅ Connected as: yourname#0Manual MCP config (if auto-config fails)
Add to ~/.claude/settings.json:
{
"mcpServers": {
"discord": {
"command": "npx",
"args": ["-y", "@tensakulabs/discord-mcp"]
}
}
}Usage with Claude
Once registered, Claude can use Discord tools directly:
"What did I miss in the #general channel of the Tensaku server?"
→ Claude calls discord_list_guilds, discord_list_channels, discord_get_unread
"Reply to Alex saying I'll be there at 5pm"
→ Claude calls discord_send_message with replyToMessageIdHooks
Fire shell commands or HTTP webhooks when Discord events occur. Configure in ~/.config/discord-mcp/config.json:
{
"hooks": {
"on_mention": [
{
"type": "command",
"enabled": true,
"command": "osascript -e 'display notification \"{content}\" with title \"Mention from {author}\"'"
}
],
"on_everyone": [],
"on_here": [],
"on_message": []
}
}Hook types
| Hook | Fires when |
|------|-----------|
| on_mention | Someone directly @username mentions you |
| on_everyone | Someone uses @everyone in a server you're in |
| on_here | Someone uses @here in a server you're in |
| on_message | Any non-bot message (use sparingly — fires a lot) |
Hook config fields
| Field | Values | Description |
|-------|--------|-------------|
| type | "command" | "http" | Shell command or HTTP POST |
| enabled | true | false | Toggle without removing |
| command | string | Shell command (type: command) |
| url | string | Endpoint to POST to (type: http) |
Template variables
Available in command strings and HTTP POST body:
| Variable | Value |
|----------|-------|
| {author} | Username of message sender |
| {content} | Message text |
| {channel} | Channel ID |
| {guild} | Guild/server ID (or "dm" for DMs) |
| {is_dm} | true or false |
HTTP hook payload
{
"author": "username",
"content": "message text",
"channel": "channel-id",
"guild": "guild-id",
"is_dm": false
}OpenClaw integration
Add to OpenClaw's MCP config:
{
"mcp": {
"servers": [{
"name": "discord",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@tensakulabs/discord-mcp"]
}]
}
}Security
- Token stored in OS keychain (macOS:
securityCLI — no native module compilation needed; Linux: keytar; fallback: AES-256-CBC encrypted file) - Token never written to config files or logs
- Local files at
~/.config/discord-mcp/:messages.db,config.json,token.enc(fallback only),daemon.log
Architecture
discord-mcp/
├── src/
│ ├── index.ts MCP server entry — registers 6 tools
│ ├── cli.ts setup + status commands
│ ├── daemon.ts Discord Gateway WebSocket → SQLite ingestion + hooks
│ ├── auth.ts keychain token storage (macOS security CLI / keytar / encrypted file)
│ ├── hooks.ts hook runner — shell commands and HTTP webhooks
│ ├── config.ts config loader (~/.config/discord-mcp/config.json)
│ ├── db.ts SQLite schema + queries
│ ├── ratelimit.ts 429 backoff + Discord headers
│ ├── state.ts per-channel last-seen state
│ ├── purge.ts scheduled message retention cleanup
│ └── tools/
│ ├── list_guilds.ts
│ ├── list_channels.ts
│ ├── get_messages.ts
│ ├── get_dms.ts
│ ├── send_message.ts
│ └── get_unread.tsAll Discord API calls go through rateLimitedFetch — automatic backoff on 429.
The daemon runs as a launchd service (com.discord-mcp.daemon) on macOS, connecting to wss://gateway.discord.gg and storing all messages locally for fast unread queries.
