Skip to main content
Glama
discord-mcp

discord-mcp

by callmegautam

discord-mcp

A self-hosted MCP server that lets an AI assistant (Claude Code, Claude Desktop, any MCP client) manage a Discord server through your own bot. It runs locally in Docker, talks to Discord with your bot token, and nothing else.

Built with TypeScript, discord.js, the official MCP TypeScript SDK and Bun.

Claude Code auditing a Discord server's permissions and setting up channels from a template with discord-mcp

Tools

Tool

What it does

list_guilds

Servers the bot is in

get_guild

Server details: owner, member count, verification, boosts

edit_guild

Change name, icon, verification level or system channel

list_channels

All channels and categories

create_channel

Create a text, voice, announcement, stage or forum channel, or a category

edit_channel

Change name, topic, slowmode, age restriction, category or position

delete_channel

Delete a channel or category

clone_channel

Copy a channel with its settings and permission overwrites

list_channel_permissions

Permission overwrites on a channel or category

set_channel_permission

Allow, deny or reset permissions for a role or member on a channel

remove_channel_permission

Remove a role's or member's overwrite from a channel

list_roles

All roles with permissions, highest first

list_role_members

Members who have a role, in pages of up to 1000

create_role

Create a role with a color, permissions and sidebar display

edit_role

Change name, color, sidebar display, mentionability or permissions

delete_role

Delete a role

move_role

Place a role directly above or below another role

list_members

Members in pages of up to 1000

get_member

One member with roles and timeout status

search_members

Members whose username or nickname starts with a query

set_nickname

Set or clear a member's nickname, or the bot's own

add_member_role

Give a member a role

remove_member_role

Take a role away from a member

kick_member

Remove a member from the server

ban_user

Ban a user, member or not, and optionally delete up to 7 days of messages

unban_user

Lift a ban

list_bans

Banned users with the ban reason, in pages of up to 1000

timeout_member

Time out a member for up to 28 days

remove_timeout

End a timeout early

get_audit_log

Audit log entries, filtered by action and by who did it

list_messages

Messages in a channel, newest first, in pages of up to 100

get_message

One message with embeds, attachments and reactions

send_message

Send text and embeds, optionally as a reply

edit_message

Change the text or embeds of a message the bot sent

delete_message

Delete a message

bulk_delete_messages

Delete up to 100 messages under 14 days old, by ID or the most recent

list_pinned_messages

Pinned messages in a channel

pin_message

Pin a message

unpin_message

Unpin a message

add_reaction

React to a message as the bot

remove_reaction

Remove the bot's or another user's reaction

clear_reactions

Remove all reactions, or all of one emoji

list_reactions

Users who reacted with an emoji

list_threads

Active threads and forum posts, or a channel's archived ones

create_thread

Start a thread in a text or announcement channel, from a message or not

create_forum_post

Start a forum post with a first message and tags

edit_thread

Rename, archive, reopen, lock, retag or set slowmode on a thread

delete_thread

Delete a thread or forum post

list_invites

Active invites with uses and expiry

create_invite

Create an invite link to a channel

delete_invite

Revoke an invite

list_webhooks

Webhooks in the server or a channel, without their secret URLs

create_webhook

Create a webhook and return its URL

delete_webhook

Delete a webhook

list_emojis

Custom emojis

create_emoji

Upload a custom emoji from an image URL

delete_emoji

Delete a custom emoji

list_stickers

Custom stickers

create_sticker

Upload a custom sticker from a file URL

delete_sticker

Delete a custom sticker

list_scheduled_events

Upcoming and running events with interest counts

create_scheduled_event

Schedule a voice, stage or external event

edit_scheduled_event

Change an event, or start, end or cancel it

delete_scheduled_event

Delete an event

audit_permissions

Flag risky permission setups, with a severity and a suggested fix

export_template

Turn the server's roles, categories and channels into a JSON template

apply_template

Create the roles, categories and channels a template describes

Every tool's inputs, types and limits are listed in the tool reference.

Every tool takes an optional guild_id. Without it, the server uses DISCORD_GUILD_ID, or the only server the bot is in.

Every write tool takes an optional reason that shows up in the server audit log. Discord keeps no audit log entry for editing a message or removing reactions, so for those tools the reason only goes to this server's log on stderr.

send_message doesn't let @everyone, @here or role mentions ping anyone unless allow_mass_mentions is true.

The message tools also work inside threads and forum posts, pass the thread ID as channel_id.

Webhook URLs contain a secret token, so list_webhooks leaves them out and only create_webhook returns the URL of the webhook it made. create_emoji, create_sticker, create_webhook and edit_guild take https URLs only, the file is downloaded and checked against Discord's size limit before it is uploaded.

Related MCP server: MCP-Discord

Resources

Resource

What it holds

discord://guilds/{guild_id}/snapshot

A server's settings, roles with permissions and member counts, and channels with their permission overwrites, as JSON

The client can attach a snapshot to the conversation, so the assistant knows the server's layout without a chain of tool calls. One snapshot is listed per server the bot is in, limited to ALLOWED_GUILD_IDS.

Prompts

Prompt

What it does

audit_server_permissions

Runs audit_permissions, explains each finding and proposes fixes as tool calls, without applying them

setup_community_server

Drafts roles, categories and channels for a described community, dry runs them and applies them after a yes

audit_permissions checks for dangerous permissions on @everyone, server wide and in channel overwrites, roles and bots with Administrator, staff or log channels that everyone can see, verification level, content filter and moderator 2FA, and powerful roles the bot sits below. It only reads, so it stays available in read-only mode. setup_community_server is left out when READ_ONLY is on or apply_template is disabled.

Templates

A template is one JSON object that describes a layout. Overwrites refer to roles by name, so the same template works on any server:

{
  "roles": [
    {
      "name": "Moderator",
      "color": "#e67e22",
      "hoist": true,
      "permissions": ["KickMembers", "ManageMessages"]
    },
    { "name": "Member" }
  ],
  "categories": [
    {
      "name": "Staff",
      "overwrites": [
        { "role": "@everyone", "deny": ["ViewChannel"] },
        { "role": "Moderator", "allow": ["ViewChannel"] }
      ],
      "channels": [{ "name": "mod-log" }, { "name": "mod-chat" }]
    },
    {
      "name": "Community",
      "channels": [
        { "name": "general", "topic": "Say hi" },
        { "name": "Lounge", "type": "voice" }
      ]
    }
  ],
  "channels": [{ "name": "welcome" }]
}

apply_template only adds. Roles, categories and channels whose name already exists are skipped, and nothing is edited or deleted, so running it twice is safe. The whole template is checked before the first change: unknown roles in overwrites, duplicate names, fields a channel type doesn't have, Community-only channel types, and permissions the bot doesn't hold itself all fail with one error listing every problem. dry_run: true reports what would be created and skipped without changing anything.

Roles are listed highest first and keep that order, placed just above @everyone. A channel without overwrites takes its category's permissions, and a channel with overwrites gets the category's plus its own, its own winning for the same role.

export_template turns a server into a template, to save the layout to a file or copy it to another server. Bot roles, member overwrites and channel types templates don't cover are left out and listed in notes.

Safety

The bot can only do what its Discord permissions allow. These settings narrow that down further:

Variable

What it does

READ_ONLY

true registers only tools marked readOnlyHint. Nothing that can change the server is exposed.

ALLOWED_GUILD_IDS

Comma separated server IDs. Other servers the bot is in are hidden and refused.

DISABLED_TOOLS

Comma separated tool names to switch off, for example list_members,get_member.

A typo in any of them stops the server at startup instead of being silently ignored: an unknown tool name, an ID that is not a server the bot is in, or a DISCORD_GUILD_ID that is not on the allowlist.

Every tool declares MCP annotations, and startup refuses a tool that doesn't: readOnlyHint on all of them, destructiveHint on anything that writes, and a reason argument for the audit log on anything destructive. Read-only mode is driven by those same annotations, so it can't drift from what the client is told.

Write tools check permissions and role order before calling Discord. When the bot lacks a permission, or a role or member sits at or above the bot's highest role, the error says which one and how to fix it instead of returning a bare 403.

Setup

1. Create the bot

  1. Create an application at https://discord.com/developers/applications and add a bot under the Bot tab.

  2. Copy the bot token.

  3. Under Privileged Gateway Intents, enable Server Members Intent. To let the message tools read what other users wrote, also enable Message Content Intent and set MESSAGE_CONTENT=true. Without it every other tool works, and messages from other users come back with empty content.

  4. Invite the bot from OAuth2 > URL Generator with the bot scope and the permissions you want it to have. The channel tools need Manage Channels, the permission overwrite and role tools need Manage Roles, the moderation tools need Kick Members, Ban Members, Moderate Members (timeouts), Manage Nicknames and View Audit Log, the message tools need View Channel, Read Message History, Send Messages, Embed Links, Add Reactions, Pin Messages and Manage Messages (deleting other users' messages, bulk deletes, removing other users' reactions), the thread tools need Create Public Threads, Create Private Threads, Send Messages in Threads and Manage Threads, edit_guild and list_invites need Manage Server, create_invite needs Create Invite, the webhook tools need Manage Webhooks, the emoji and sticker tools need Create Expressions and Manage Expressions (deleting ones the bot didn't upload), and the event tools need Create Events and Manage Events (changing events the bot didn't create). Discord only lets the bot give out permissions it has itself, unless it is an Administrator.

  5. In your server settings, drag the bot's role above any role you want it to manage. Role tools refuse roles at or above the bot's highest role, and roles managed by an integration. Moderation tools refuse members whose highest role is at or above the bot's, and the server owner.

2. Configure

cp .env.example .env
chmod 600 .env

Put the token in .env:

DISCORD_TOKEN=your-bot-token
DISCORD_GUILD_ID=optional-default-server-id
ALLOWED_GUILD_IDS=
READ_ONLY=false
MESSAGE_CONTENT=false
DISABLED_TOOLS=
LOG_LEVEL=info

See Safety for ALLOWED_GUILD_IDS, READ_ONLY and DISABLED_TOOLS. MESSAGE_CONTENT=true requests the Message Content intent at login, which fails if it is not enabled in the developer portal.

.env is git ignored and docker ignored. The token is passed to the container at runtime and is never baked into the image.

3. Get the image

Pull the published image from GitHub Container Registry:

docker pull ghcr.io/callmegautam/discord-mcp:1

Images are tagged with the full version (1.3.0), the minor (1.3), the major (1) and latest, for linux/amd64 and linux/arm64.

Or build it yourself from a checkout:

docker build -t discord-mcp .

The commands below use the published image, replace it with discord-mcp if you built your own.

4. Connect it to Claude Code

claude mcp add discord -s user -- docker run --rm -i --env-file /absolute/path/to/discord-mcp/.env ghcr.io/callmegautam/discord-mcp:1

Run /mcp in Claude Code to confirm it is connected.

For other MCP clients, use the same command:

{
  "mcpServers": {
    "discord": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "--env-file",
        "/absolute/path/to/discord-mcp/.env",
        "ghcr.io/callmegautam/discord-mcp:1"
      ]
    }
  }
}

Or run it as a long-lived HTTP server

By default every MCP client starts its own container over stdio. With TRANSPORT=http one container keeps running, logs in to Discord once, and serves any number of clients over Streamable HTTP, each in its own session.

Every request needs a bearer token, and the server refuses to start in HTTP mode without one. Generate a token and add it to .env:

echo "HTTP_TOKEN=$(openssl rand -hex 32)" >> .env

Start the container, published on localhost only:

docker run -d --name discord-mcp --restart unless-stopped \
  --env-file /absolute/path/to/discord-mcp/.env -e TRANSPORT=http \
  -p 127.0.0.1:3000:3000 \
  --health-cmd "wget -qO- http://127.0.0.1:3000/health || exit 1" \
  ghcr.io/callmegautam/discord-mcp:1

Then point Claude Code at it:

claude mcp add --transport http -s user \
  --header "Authorization: Bearer your-http-token" \
  discord http://127.0.0.1:3000/mcp

Variable

What it does

TRANSPORT

stdio (default) or http

HTTP_TOKEN

Bearer token clients must send, at least 32 characters. Required with TRANSPORT=http

HTTP_HOST

Address to listen on. Defaults to 127.0.0.1, and to 0.0.0.0 in the Docker image

HTTP_PORT

Port to listen on, defaults to 3000

The MCP endpoint is /mcp. /health answers without a token, for container health checks. Anyone with the token can use every tool the bot exposes, so keep the port off the public internet: publish it on 127.0.0.1 as above, or put the server behind a reverse proxy with TLS. Sessions idle for an hour are closed, and clients start a new one on their next request.

Development

Requires Bun 1.4 or newer.

bun install
bun run dev        # run locally with watch mode, reads .env automatically
bun run check      # typecheck, lint, format check and tests
bun run docs:tools # regenerate docs/tools.md after changing a tool

docs/tools.md is generated from the tool schemas, and bun run check fails when it is out of date.

To try the tools by hand, use the MCP inspector:

bunx @modelcontextprotocol/inspector bun run src/index.ts

Integration tests

bun run check only runs unit tests. The integration tests start the real server over stdio and call every write tool through an MCP client against a real Discord server: they create and delete channels, roles, messages, threads, invites, webhooks, emojis, stickers and events, rename the server and set its system channel, then put everything back.

Run them only against a throwaway server you made for this:

  1. Create an empty server and invite the bot with Administrator, then drag its role to the top.

  2. Run the tests with the server ID:

INTEGRATION_GUILD_ID=your-test-server-id bun run test:integration

They use the token from .env, lock the server to that one ID through ALLOWED_GUILD_IDS, and refuse to run when it has more than 10 members. Everything they create is named with an mcpit prefix, and anything left over from an interrupted run is deleted at the start of the next one.

Layout

src/
  index.ts          entry point: config, Discord login, MCP over stdio or HTTP
  http.ts           Streamable HTTP server with bearer token auth and per-session MCP servers
  server.ts         builds the MCP server with its tools, resources and prompts
  config.ts         environment validation
  discord/          Discord client, guild resolution, permission checks, response shaping
  tools/            one file per tool group, registered in tools/index.ts, policy.ts
                    enforces annotations, READ_ONLY and DISABLED_TOOLS
  resources/        MCP resources (server snapshot)
  prompts/          MCP prompts
  lib/              logger, shared schemas, tool result helpers
scripts/            tool reference generator
docs/               generated tool reference
tests/              bun test suites, integration/ for the live server tests

stdout carries the MCP protocol. Log through src/lib/logger.ts, which writes to stderr, never with console.log.

Community

Built for running The Basement, my Discord server for devs. Come say hi, or ask about the MCP there.

License

MIT

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      A
      maintenance
      Enables AI assistants to interact with Discord through a bot, supporting server and member inspection, channel and role management, message and reaction handling, forum operations, and webhook administration.
      827 npm
      MIT
    • A
      license
      Not graded
      quality
      A
      maintenance
      Enables AI assistants to interact with Discord by managing servers, channels, roles, members, messages, forum posts, reactions, webhooks, and permissions.
      827 npm
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI assistants to operate a chat server by reading, sending, and managing messages, direct messages, reactions, channels, roles, and members through bot tools.
      PolyForm Noncommercial 1.0.0
    • A
      license
      Not graded
      quality
      A
      maintenance
      Enables AI assistants to interact with the Discord platform through a bot login. Supports listing servers and members, managing channels, roles, forum posts, messages, reactions, and webhooks.
      827 npm
      MIT