Gautam Suthar 編寫 discord-mcp,避免把 Discord 機械人 token 交給陌生程式
I didn't want a stranger's code holding my Discord bot token, so I wrote my own MCP server
Gautam Suthar 自行開發並以 MIT 授權發布 discord-mcp,讓 Claude Code 管理 Discord 伺服器,避免把具管理員權限的機械人 token 交給未讀過的程式。
I wanted Claude Code to manage my Discord server. Create channels, sort out roles, clean up messages, check the audit log, the boring admin stuff.
Community Discord MCP servers already exist. Every one of them needs your bot token. My bot has admin-level permissions on a server with real people in it. Handing that token to code I hadn't read felt like a bad trade for saving a weekend.
So I wrote my own. It's small enough to read end to end, it runs locally in Docker, and it only does what I put in it.
It's called discord-mcp, it's MIT licensed, and it's on 1.3.
What it does
67 tools across the parts of Discord you'd actually manage:
- channels and categories, including permission overwrites and cloning
- roles: create, edit, reorder, assign
- moderation: kick, ban, timeout, nicknames, audit log
- messages: read, send with embeds, edit, delete, bulk delete, pins, reactions
- threads and forum posts
- invites, webhooks, emojis, stickers, scheduled events, server settings
- a permission audit, and server templates you can export and apply
The direction matters: this isn't a way to talk to Claude from Discord. Claude manages the server.
Safety first, then write tools
The first release with write tools came after a release that was only safety rails. If something is going to delete channels on my server, the guard rails come first.
Three settings narrow down what the bot can do beyond its Discord permissions:
| Variable | What it does |
|---|---|
READ_ONLY |
true exposes only read-only tools. Nothing can change. |
ALLOWED_GUILD_IDS |
Servers the bot may touch. Every other server is hidden. |
DISABLED_TOOLS |
Switch off single tools by name. |
A typo in any of them stops the server at startup. An unknown tool name or a server ID the bot isn't in is an error, not something silently ignored.
Read-only mode can't drift
The usual way to build a read-only mode is a list of "write tools" to skip. That list goes stale the first time someone adds a tool and forgets it.
discord-mcp uses the MCP annotations instead. Every tool has to declare readOnlyHint, every write tool has to declare destructiveHint, and every destructive tool has to accept a reason that ends up in the Discord audit log. Startup refuses any tool that breaks those rules.
Read-only mode then just drops anything not marked read-only. The client and the filter read the same annotations, so they can't disagree.
The tools I actually use
The CRUD tools are the boring part. Two newer ones are why I keep it running.
audit_permissions reads the whole server and flags what's risky: dangerous permissions on @everyone, roles and bots with Administrator, staff channels everyone can see, verification level, content filter, moderator 2FA. It only reads, so it works in read-only mode too. The first time I ran it on my test server it flagged the bot's own Administrator role. Fair.
export_template and apply_template turn a server's roles, categories and channels into JSON and back. Apply only adds, never edits or deletes, and skips anything that already exists, so running it twice is safe. The whole template gets checked before the first change, with dry_run if you just want to see what would happen. There's also a setup_community_server prompt: describe the community you want, Claude drafts the layout, dry runs it, and applies it after you say yes.
Errors that tell you what to fix
Discord's answer to a missing permission is a bare 403. Write tools check permissions and role order before calling Discord, so you get this instead: which permission the bot is missing, or which role sits at or above the bot's highest role, and how to fix it.
Tested against a real server
Unit tests cover the logic. They can't tell you how Discord actually behaves.
So there are 35 integration tests that spawn the server over stdio and call tools through the MCP SDK client, the same path Claude uses, against a throwaway test server. The harness locks itself to that one server and refuses any server with more than 10 members, so a wrong ID can't hit a real community.
They found two bugs the unit tests never would have:
-
list_role_membersfetched all members on every call. Discord rate limits that to about once per 30 seconds per server, so the second call failed. Now it loads members once and the cache stays current after that. -
get_messagekept showing reactions that had already been removed. Discord leaves outreactionswhen there are none, and discord.js only patches its cached copy with fields that are present. Dropping the cached message before fetching fixed it.
Try it
You need a Discord bot with the permissions you want it to have. The README walks through that.
docker pull ghcr.io/callmegautam/discord-mcp:1
Put your token in a .env and add it to Claude Code:
claude mcp add discord -s user -- docker run --rm -i --env-file /absolute/path/to/.env ghcr.io/callmegautam/discord-mcp:1
It works with any MCP client, same command. Start with READ_ONLY=true if you just want it to look around first.
- Repo: https://github.com/callmegautam/discord-mcp
- Tool reference: https://github.com/callmegautam/discord-mcp/blob/main/docs/tools.md
There's also an HTTP transport now (TRANSPORT=http with a bearer token) if you'd rather run one long-lived container than spawn one per session.
If something breaks on your server, open an issue. The things I couldn't test live yet are kick, ban and timeout on a real member, announcement and stage channels, and private threads.
I hang out with other devs in The Basement, my Discord server. Questions, side projects, and a contributor cup every month or so. https://discord.gg/BqAK3RNWeP
來源:DEV Community · dev.to
