Access and scope
Use these settings to give the server the credential and the smallest tool surface your agent needs.
Bot credential
Section titled “Bot credential”DISCORD_TOKEN
Section titled “DISCORD_TOKEN”- Type: string, minimum 50 characters
- Default: required
- Use: Discord bot token for REST authentication. Bare tokens and
Bot <token>are accepted; the token is redacted from audit events. - Example:
DISCORD_TOKEN=your-discord-bot-token
DISCORD_EXPECTED_BOT_ID
Section titled “DISCORD_EXPECTED_BOT_ID”- Type: Discord snowflake with 17–20 digits
- Default: unset for backward compatibility
- Use: Fails startup when
DISCORD_TOKENbelongs to a different bot. - Example:
DISCORD_EXPECTED_BOT_ID=987654321098765432
discord-mcp init --discover-guilds derives this non-secret lock from
Discord. When set, the server verifies /users/@me before serving MCP and
caches the successful result for the process. Keep the value stable during a
normal token rotation; a mismatch usually means the wrong application’s token
was supplied. This identity lock authorizes every bot-application route
(application emojis, application commands, role-connection metadata, SKUs,
entitlements, and the current application): those tools can affect the bot’s
application globally, never another application. They require the lock even
when ALLOWED_GUILDS is unset. See Credential lifecycle.
Remote HTTP credential
Section titled “Remote HTTP credential”DISCORD_MCP_ACCESS_TOKEN
Section titled “DISCORD_MCP_ACCESS_TOKEN”- Type: string, minimum 32 characters
- Default: required only for
discord-mcp serve --http - Use: Shared bearer credential for requests to the remote
/mcpendpoint. It is independent of the Discord bot token. - Example:
DISCORD_MCP_ACCESS_TOKEN=replace-with-a-long-random-secret
Send it as Authorization: Bearer <DISCORD_MCP_ACCESS_TOKEN>. Use a high-entropy
secret stored in a secret manager. This credential authorizes every client with
the same Discord bot identity; it is not per-user OAuth.
MCP_HTTP_MAX_BODY_BYTES
Section titled “MCP_HTTP_MAX_BODY_BYTES”- Type: integer from 1,024 through 67,108,864
- Default:
4194304(4 MiB) - Use: Bounds the actual request bytes before the MCP SDK buffers or parses them.
- Example:
MCP_HTTP_MAX_BODY_BYTES=8388608
Oversized declared and chunked bodies receive 413 Payload Too Large. The
default accommodates Discord’s base64 asset inputs without allowing one caller
to allocate unbounded process memory.
MCP_HTTP_MAX_IN_FLIGHT
Section titled “MCP_HTTP_MAX_IN_FLIGHT”- Type: integer from 1 through 1,024
- Default:
16 - Use: Caps authenticated HTTP exchanges being processed at once.
- Example:
MCP_HTTP_MAX_IN_FLIGHT=32
Overflow receives 503 Service Unavailable with Retry-After: 1; the server
does not retain an internal queue. This is a process bulkhead, not a per-user
rate limit. Keep a caller-aware rate and timeout policy at the public proxy.
For TLS, reverse-proxy, Responses API, and Codex setup, see OpenAI remote MCP.
Guild default
Section titled “Guild default”DISCORD_DEFAULT_GUILD_ID
Section titled “DISCORD_DEFAULT_GUILD_ID”- Type: Discord snowflake with 17–20 digits
- Default: unset
- Use: Supplies an omitted top-level
guild_idbefore tool validation. An explicit tool argument always wins. - Example:
DISCORD_DEFAULT_GUILD_ID=111122223333444455
This does not infer a guild from a channel, user, webhook, or message ID, and it
does not affect tools without a top-level guild_id input.
When ALLOWED_GUILDS is also set, the default guild must appear in that
allowlist or startup fails.
Blueprint checkpoint state
Section titled “Blueprint checkpoint state”MCP_BLUEPRINT_STATE_DIR
Section titled “MCP_BLUEPRINT_STATE_DIR”- Type: non-empty directory path
- Default: the platform-specific local user-state directory
- Use: Stores authenticated caller-local plan envelopes, append-only checkpoints, apply locks, and completion-only Activity Evidence for the blueprint workflow.
- Example:
MCP_BLUEPRINT_STATE_DIR=/var/lib/discord-mcp/blueprints
Use a private persistent directory when running in a container. Plan envelopes
store the trusted payload behind a short plan_ref; checkpoints contain Discord
target IDs and resolved bindings; Activity Evidence stores the trusted blueprint
needed for restart-safe read-only verification. None contains the bot token or
raw plan_token, but blueprint names and Components V2 content are local
application data and should remain private. Every process that resolves the same
caller-local reference must share this protected, correctly locked directory; a
stateless multi-replica deployment can keep using the legacy self-contained
plan_token. On Windows, the service account’s directory ACL is the
confidentiality boundary; do not grant other local users access. The reference
cache is bounded to 256 records and 64 MiB, and fails back to the legacy token
without automatically evicting resumable work. A Discord bot-token rotation invalidates pending references,
checkpoints, and saved evidence, and an HTTP access-token rotation invalidates
state created through that HTTP caller boundary. Create and apply a fresh
guild_blueprint_plan after either rotation. See
Safe guild blueprints.
Guild boundary
Section titled “Guild boundary”ALLOWED_GUILDS
Section titled “ALLOWED_GUILDS”- Type: comma-separated Discord snowflakes with 17–20 digits
- Default: unset, preserving access to every guild visible to the bot
- Use: Enforces which Discord servers this deployment may operate against.
- Example:
ALLOWED_GUILDS=111122223333444455,999000999000999000
The boundary is enforced server-side after input validation. A direct
guild_id is checked without a Discord request. Channel, thread, webhook, and
invite IDs are resolved to a guild before the actual operation; successful
resolutions are kept in bounded caches because those resources cannot move
between guilds.
Guild stickers are checked after lookup, while standard global stickers remain
readable.
When the boundary is active, global writes and interaction-token routes whose
guild cannot be proven before execution are omitted from tools/list and
rejected on direct calls with GUILD_SCOPE_UNRESOLVED. A known but disallowed
guild returns GUILD_NOT_ALLOWED. The application-emoji operations are a
bot-scoped exception when DISCORD_EXPECTED_BOT_ID is set: they may target only
the locked bot application (and may omit application_id to resolve it from
/applications/@me). Other application/global writes remain unavailable.
Pipeline steps and progressive dispatchers re-enter the same policy. Gateway
subscriptions for guild, voice, and channel URIs are checked before
registration.
Global read-only discovery remains available, including
users_list_current_user_guilds; its results do not extend the allowlist. If
you need one of the intentionally blocked global writes, use a separate
deployment whose scope is explicit instead of weakening a shared bot runtime.
Tool scope
Section titled “Tool scope”MCP_CATEGORIES
Section titled “MCP_CATEGORIES”- Type: comma-separated tool category names
- Default: unset, so every category is available
- Use: Omits disallowed categories from
tools/listand rejects a direct call withSCOPE_REJECTED. - Example:
MCP_CATEGORIES=messages,channels,members
Entries are trimmed and empty values are ignored. Unknown categories fail at
startup with the valid list instead of silently disabling a tool surface. The
meta category is always available so mcp_pipeline remains reachable.
MCP_TOOL_SURFACE
Section titled “MCP_TOOL_SURFACE”- Type:
fullorprogressive - Default:
full - Use: Controls how much of the already-authorized catalog is advertised to the model.
- Example:
MCP_TOOL_SURFACE=progressive
full advertises the existing 209-tool catalog for maximum client
compatibility. progressive advertises the direct read-only
build_discord_server architecture front door plus guild_blueprint_apply and
guild_blueprint_evidence when guild architecture is authorized, together with
mcp_tools_search, mcp_tools_read, mcp_tools_write, and
mcp_tools_destructive. The front door is a compact alias of canonical
guild_blueprint_plan; the other two direct tools complete and independently verify an approved
plan. The model searches for compact matches for other tools. A single match already carries its
contract; for multiple matches, it searches the selected exact tool name before using the dispatcher
named by that result. Set detail: "full" on mcp_tools_search
only when several full contracts are necessary in one response. The dispatchers
expose accurate host approval annotations and reject a tool sent through the
wrong risk route. This substantially reduces both the initial tools/list
payload and the common ambiguous-search payload for clients without native
deferred MCP loading.
This setting is not an authorization control. Search only returns tools allowed
by ALLOWED_GUILDS and MCP_CATEGORIES, and every dispatched call still passes
validation, guild scope, category, confirmation, dry-run, audit, and resilience
middleware. Keep both allowlists narrow even when progressive discovery is
enabled.
The current catalogues classify all 209 tools. Fifteen contracts are
colocated directly on tool definitions; a reviewed central registry covers all
208 Discord routes for credential-free reports. The users_create_dm route is
scope: user and has a separate recipient-bound caller-approval policy
controlled by MCP_DM_CONSENT_MODE; require enforces a one-time approval.
The three blueprint
lifecycle tools are marked as delegated because their own target-bound
permission, drift, and readback verifiers remain authoritative. Unknown is
never treated as ready.
For a read-only capability report before a planned write, run
discord-mcp doctor --access --guild-id <guild-id> --json. It verifies the
locked bot identity, guild membership, role bitfields, optional channel
overwrites, and the privileged intent flags visible on the application. The
intents object keeps that application-side approval/availability separate
from the privileged intents requested by this running Gateway client; the
current client requests none of those privileged intents, so application flags
must not be read as proof of runtime Gateway state. The report is advisory in
this rollout: unknown evidence is kept separate from denied, and it does
not replace the server-side authorization middleware. tool_access is an
explicitly labelled catalogued subset (tool_access_scope: catalogued_subset,
with its tool_access_catalogued_count). Future tools absent from the reviewed
catalog are reported as unknown rather than silently admitted. A guild owner or
Administrator is treated as complete evidence for the declared known permission
bits even if Discord returns a newer unknown bit; that bit remains visible in
unknown_permission_bits for upgrade diagnostics.
For field-dependent tools such as members_modify, the report uses
conditional until an exact action payload is available; the runtime gate then
derives only the permissions for fields that are actually present.
Message readers and intelligence tools that return user-authored message
content also declare MESSAGE_CONTENT. The runtime gate does not infer this
from a successful REST response: without explicit privileged-intent evidence,
enforce keeps the call unresolved or denied.
The permission catalogue tracks Discord’s current split permissions: guild
emoji/sticker/sound creation uses CREATE_GUILD_EXPRESSIONS, scheduled-event
creation uses CREATE_EVENTS, pin/unpin uses PIN_MESSAGES, and stage-instance
operations include the full moderator set (MANAGE_CHANNELS, MUTE_MEMBERS, and
MOVE_MEMBERS). This keeps the preflight report aligned with the
post-February-2026 Discord API rather than silently treating older manage-only
permissions as sufficient.
When ALLOWED_GUILDS is active, users_create_dm stays hidden by default
because a guild allowlist is not consent to contact arbitrary users. Set
MCP_ALLOW_USER_SCOPED=true together with DISCORD_EXPECTED_BOT_ID only when
the deployment intentionally exposes user-scoped DM creation; the separate DM
caller-approval gate still applies.

