Skip to content

Access and scope

Use these settings to give the server the credential and the smallest tool surface your agent needs.

  • 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
  • Type: Discord snowflake with 17–20 digits
  • Default: unset for backward compatibility
  • Use: Fails startup when DISCORD_TOKEN belongs 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.

  • Type: string, minimum 32 characters
  • Default: required only for discord-mcp serve --http
  • Use: Shared bearer credential for requests to the remote /mcp endpoint. 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.

  • 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.

  • 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.

  • Type: Discord snowflake with 17–20 digits
  • Default: unset
  • Use: Supplies an omitted top-level guild_id before 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.

  • 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.

  • 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.

  • Type: comma-separated tool category names
  • Default: unset, so every category is available
  • Use: Omits disallowed categories from tools/list and rejects a direct call with SCOPE_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.

  • Type: full or progressive
  • 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.